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;
58const DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS: u16 = 4;
60
61pub(crate) const CDN_BASE_URL: &str = "https://blocks.aleo.org";
63
64#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
66pub struct BondedBalances(IndexMap<String, (String, String, u64)>);
67
68impl FromStr for BondedBalances {
69 type Err = serde_json::Error;
70
71 fn from_str(s: &str) -> Result<Self, Self::Err> {
72 serde_json::from_str(s)
73 }
74}
75
76#[derive(Clone, Debug, Parser)]
78pub struct Start {
79 #[clap(default_value = "0", long = "network")]
81 pub network: u16,
82
83 #[clap(long = "validator")]
85 pub validator: bool,
86 #[clap(long = "prover")]
88 pub prover: bool,
89 #[clap(long = "client")]
91 pub client: bool,
92
93 #[clap(long = "private-key")]
95 pub private_key: Option<String>,
96 #[clap(long = "private-key-file")]
98 pub private_key_file: Option<PathBuf>,
99
100 #[clap(long = "node")]
102 pub node: Option<SocketAddr>,
103 #[clap(long = "bft")]
105 pub bft: Option<SocketAddr>,
106 #[clap(default_value = "", long = "peers")]
108 pub peers: String,
109 #[clap(default_value = "", long = "validators")]
111 pub validators: String,
112 #[clap(long = "allow-external-peers")]
114 pub allow_external_peers: bool,
115 #[clap(long = "rotate-external-peers")]
117 pub rotate_external_peers: bool,
118
119 #[clap(long = "rest")]
121 pub rest: Option<SocketAddr>,
122 #[clap(default_value = "10", long = "rest-rps")]
124 pub rest_rps: u32,
125 #[clap(long)]
127 pub norest: bool,
128
129 #[clap(long)]
131 pub nodisplay: bool,
132 #[clap(default_value = "1", long = "verbosity")]
134 pub verbosity: u8,
135 #[clap(default_value_os_t = std::env::temp_dir().join("snarkos.log"), long = "logfile")]
137 pub logfile: PathBuf,
138
139 #[clap(default_value = "false", long = "metrics")]
141 pub metrics: bool,
142 #[clap(long = "metrics-ip")]
144 pub metrics_ip: Option<SocketAddr>,
145
146 #[clap(long = "storage")]
148 pub storage: Option<PathBuf>,
149 #[clap(long = "cdn")]
151 pub cdn: Option<String>,
152 #[clap(long)]
154 pub nocdn: bool,
155
156 #[clap(long)]
158 pub dev: Option<u16>,
159 #[clap(long)]
161 pub dev_num_validators: Option<u16>,
162 #[clap(default_value = "false", long = "no-dev-txs")]
164 pub no_dev_txs: bool,
165 #[clap(long)]
167 pub dev_bonded_balances: Option<BondedBalances>,
168}
169
170impl Start {
171 pub fn parse(self) -> Result<String> {
173 let shutdown: Arc<AtomicBool> = Default::default();
175
176 let log_receiver =
178 crate::helpers::initialize_logger(self.verbosity, self.nodisplay, self.logfile.clone(), shutdown.clone());
179 Self::runtime().block_on(async move {
181 let mut cli = self.clone();
183 match cli.network {
185 MainnetV0::ID => {
186 let node = cli.parse_node::<MainnetV0>(shutdown.clone()).await.expect("Failed to parse the node");
188 if !cli.nodisplay {
190 Display::start(node, log_receiver).expect("Failed to initialize the display");
192 }
193 }
194 TestnetV0::ID => {
195 let node = cli.parse_node::<TestnetV0>(shutdown.clone()).await.expect("Failed to parse the node");
197 if !cli.nodisplay {
199 Display::start(node, log_receiver).expect("Failed to initialize the display");
201 }
202 }
203 CanaryV0::ID => {
204 let node = cli.parse_node::<CanaryV0>(shutdown.clone()).await.expect("Failed to parse the node");
206 if !cli.nodisplay {
208 Display::start(node, log_receiver).expect("Failed to initialize the display");
210 }
211 }
212 _ => panic!("Invalid network ID specified"),
213 };
214 std::future::pending::<()>().await;
217 });
218
219 Ok(String::new())
220 }
221}
222
223impl Start {
224 fn parse_trusted_peers(&self) -> Result<Vec<SocketAddr>> {
226 match self.peers.is_empty() {
227 true => Ok(vec![]),
228 false => Ok(self
229 .peers
230 .split(',')
231 .flat_map(|ip| match ip.parse::<SocketAddr>() {
232 Ok(ip) => Some(ip),
233 Err(e) => {
234 eprintln!("The IP supplied to --peers ('{ip}') is malformed: {e}");
235 None
236 }
237 })
238 .collect()),
239 }
240 }
241
242 fn parse_trusted_validators(&self) -> Result<Vec<SocketAddr>> {
244 match self.validators.is_empty() {
245 true => Ok(vec![]),
246 false => Ok(self
247 .validators
248 .split(',')
249 .flat_map(|ip| match ip.parse::<SocketAddr>() {
250 Ok(ip) => Some(ip),
251 Err(e) => {
252 eprintln!("The IP supplied to --validators ('{ip}') is malformed: {e}");
253 None
254 }
255 })
256 .collect()),
257 }
258 }
259
260 fn parse_cdn<N: Network>(&self) -> Option<String> {
262 let is_no_node_type = !(self.validator || self.prover || self.client);
264
265 if self.dev.is_some() || self.nocdn || self.prover || is_no_node_type {
271 None
272 }
273 else {
275 match &self.cdn {
277 Some(cdn) => match cdn.is_empty() {
279 true => None,
280 false => Some(cdn.clone()),
281 },
282 None => match N::ID {
284 MainnetV0::ID => Some(format!("{CDN_BASE_URL}/mainnet/v0")),
285 TestnetV0::ID => Some(format!("{CDN_BASE_URL}/testnet/v0")),
286 CanaryV0::ID => Some(format!("{CDN_BASE_URL}/canary/v0")),
287 _ => None,
288 },
289 }
290 }
291 }
292
293 fn parse_private_key<N: Network>(&self) -> Result<Account<N>> {
296 match self.dev {
297 None => match (&self.private_key, &self.private_key_file) {
298 (Some(private_key), None) => Account::from_str(private_key.trim()),
300 (None, Some(path)) => {
302 check_permissions(path)?;
303 Account::from_str(std::fs::read_to_string(path)?.trim())
304 }
305 (None, None) => match self.client {
307 true => Account::new(&mut rand::thread_rng()),
308 false => bail!("Missing the '--private-key' or '--private-key-file' argument"),
309 },
310 (Some(_), Some(_)) => {
312 bail!("Cannot use '--private-key' and '--private-key-file' simultaneously, please use only one")
313 }
314 },
315 Some(dev) => {
316 Account::try_from({
318 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
320 for _ in 0..dev {
322 let _ = PrivateKey::<N>::new(&mut rng)?;
323 }
324 let private_key = PrivateKey::<N>::new(&mut rng)?;
325 println!("🔑 Your development private key for node {dev} is {}.\n", private_key.to_string().bold());
326 private_key
327 })
328 }
329 }
330 }
331
332 fn parse_development(
334 &mut self,
335 trusted_peers: &mut Vec<SocketAddr>,
336 trusted_validators: &mut Vec<SocketAddr>,
337 ) -> Result<()> {
338 if let Some(dev) = self.dev {
342 if trusted_peers.is_empty() {
344 for i in 0..dev {
345 if i != dev {
346 trusted_peers.push(SocketAddr::from_str(&format!("127.0.0.1:{}", 4130 + i))?);
347 }
348 }
349 }
350 if trusted_validators.is_empty() {
352 for i in 0..2 {
354 if i != dev {
355 trusted_validators.push(SocketAddr::from_str(&format!("127.0.0.1:{}", MEMORY_POOL_PORT + i))?);
356 }
357 }
358 }
359 if self.node.is_none() {
363 self.node = Some(SocketAddr::from_str(&format!("0.0.0.0:{}", 4130 + dev))?);
364 }
365
366 if !self.norest && self.rest.is_none() {
368 self.rest = Some(SocketAddr::from_str(&format!("0.0.0.0:{}", 3030 + dev)).unwrap());
369 }
370 }
371 Ok(())
372 }
373
374 fn parse_genesis<N: Network>(&self) -> Result<Block<N>> {
377 if self.dev.is_some() {
378 let num_committee_members = match self.dev_num_validators {
380 Some(num_committee_members) => num_committee_members,
381 None => DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
382 };
383 ensure!(
384 num_committee_members >= DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
385 "Number of genesis committee members is too low"
386 );
387
388 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
390 let dev_keys =
392 (0..num_committee_members).map(|_| PrivateKey::<N>::new(&mut rng)).collect::<Result<Vec<_>>>()?;
393 let development_addresses = dev_keys.iter().map(Address::<N>::try_from).collect::<Result<Vec<_>>>()?;
395
396 let (committee, bonded_balances) = match &self.dev_bonded_balances {
398 Some(bonded_balances) => {
399 let bonded_balances = bonded_balances
401 .0
402 .iter()
403 .map(|(staker_address, (validator_address, withdrawal_address, amount))| {
404 let staker_addr = Address::<N>::from_str(staker_address)?;
405 let validator_addr = Address::<N>::from_str(validator_address)?;
406 let withdrawal_addr = Address::<N>::from_str(withdrawal_address)?;
407 Ok((staker_addr, (validator_addr, withdrawal_addr, *amount)))
408 })
409 .collect::<Result<IndexMap<_, _>>>()?;
410
411 let mut members = IndexMap::new();
413 for (staker_address, (validator_address, _, amount)) in bonded_balances.iter() {
414 match staker_address == validator_address {
416 true => ensure!(amount >= &MIN_VALIDATOR_STAKE, "Validator stake is too low"),
417 false => ensure!(amount >= &MIN_DELEGATOR_STAKE, "Delegator stake is too low"),
418 }
419
420 ensure!(
422 development_addresses.contains(validator_address),
423 "Validator address {validator_address} is not included in the list of development addresses"
424 );
425
426 members.entry(*validator_address).and_modify(|(stake, _, _)| *stake += amount).or_insert((
428 *amount,
429 true,
430 rng.gen_range(0..100),
431 ));
432 }
433 let committee = Committee::<N>::new(0u64, members)?;
435 (committee, bonded_balances)
436 }
437 None => {
438 let stake_per_member =
440 N::STARTING_SUPPLY.saturating_div(2).saturating_div(num_committee_members as u64);
441 ensure!(stake_per_member >= MIN_VALIDATOR_STAKE, "Committee stake per member is too low");
442
443 let members = development_addresses
445 .iter()
446 .map(|address| (*address, (stake_per_member, true, rng.gen_range(0..100))))
447 .collect::<IndexMap<_, _>>();
448
449 let bonded_balances = members
452 .iter()
453 .map(|(address, (stake, _, _))| (*address, (*address, *address, *stake)))
454 .collect::<IndexMap<_, _>>();
455 let committee = Committee::<N>::new(0u64, members)?;
457
458 (committee, bonded_balances)
459 }
460 };
461
462 ensure!(
464 committee.members().len() == num_committee_members as usize,
465 "Number of committee members {} does not match the expected number of members {num_committee_members}",
466 committee.members().len()
467 );
468
469 let remaining_balance = N::STARTING_SUPPLY.saturating_sub(committee.total_stake());
471 let public_balance_per_validator = remaining_balance.saturating_div(num_committee_members as u64);
472
473 let mut public_balances = dev_keys
475 .iter()
476 .map(|private_key| Ok((Address::try_from(private_key)?, public_balance_per_validator)))
477 .collect::<Result<indexmap::IndexMap<_, _>>>()?;
478
479 let leftover =
481 remaining_balance.saturating_sub(public_balance_per_validator * num_committee_members as u64);
482 if leftover > 0 {
483 let (_, balance) = public_balances.get_index_mut(0).unwrap();
484 *balance += leftover;
485 }
486
487 let public_balances_sum: u64 = public_balances.values().copied().sum();
489 if committee.total_stake() + public_balances_sum != N::STARTING_SUPPLY {
490 bail!("Sum of committee stakes and public balances does not equal total starting supply.");
491 }
492
493 load_or_compute_genesis(dev_keys[0], committee, public_balances, bonded_balances, self.dev, &mut rng)
495 } else {
496 if self.dev_num_validators.is_some() {
498 eprintln!("The '--dev-num-validators' flag is ignored because '--dev' is not set");
499 }
500
501 Block::from_bytes_le(N::genesis_bytes())
502 }
503 }
504
505 const fn parse_node_type(&self) -> NodeType {
507 if self.validator {
508 NodeType::Validator
509 } else if self.prover {
510 NodeType::Prover
511 } else {
512 NodeType::Client
513 }
514 }
515
516 #[rustfmt::skip]
518 async fn parse_node<N: Network>(&mut self, shutdown: Arc<AtomicBool>) -> Result<Node<N>> {
519 println!("{}", crate::helpers::welcome_message());
521
522 if cfg!(feature = "test_targets") && self.dev.is_none() {
525 bail!("The 'test_targets' feature is enabled, but the '--dev' flag is not set");
526 }
527
528 let mut trusted_peers = self.parse_trusted_peers()?;
530 let mut trusted_validators = self.parse_trusted_validators()?;
532 self.parse_development(&mut trusted_peers, &mut trusted_validators)?;
534
535 let cdn = self.parse_cdn::<N>();
537
538 let genesis = self.parse_genesis::<N>()?;
540 let account = self.parse_private_key::<N>()?;
542 let node_type = self.parse_node_type();
544
545 let node_ip = match self.node {
547 Some(node_ip) => node_ip,
548 None => SocketAddr::from_str("0.0.0.0:4130").unwrap(),
549 };
550
551 let rest_ip = match self.norest {
553 true => None,
554 false => self.rest.or_else(|| Some("0.0.0.0:3030".parse().unwrap())),
555 };
556
557 if self.nodisplay {
559 println!("👛 Your Aleo address is {}.\n", account.address().to_string().bold());
561 println!(
563 "🧭 Starting {} on {} at {}.\n",
564 node_type.description().bold(),
565 N::NAME.bold(),
566 node_ip.to_string().bold()
567 );
568
569 if node_type.is_validator() {
571 if let Some(rest_ip) = rest_ip {
572 println!("🌐 Starting the REST server at {}.\n", rest_ip.to_string().bold());
573
574 if let Ok(jwt_token) = snarkos_node_rest::Claims::new(account.address()).to_jwt_string() {
575 println!("🔑 Your one-time JWT token is {}\n", jwt_token.dimmed());
576 }
577 }
578 }
579 }
580
581 #[cfg(target_family = "unix")]
583 if node_type.is_validator() {
584 crate::helpers::check_open_files_limit(RECOMMENDED_MIN_NOFILES_LIMIT);
585 }
586 crate::helpers::check_validator_machine(node_type);
588
589 if self.metrics {
591 metrics::initialize_metrics(self.metrics_ip);
592 }
593
594 let storage_mode = match &self.storage {
596 Some(path) => StorageMode::Custom(path.clone()),
597 None => StorageMode::from(self.dev),
598 };
599
600 let dev_txs = match self.dev {
602 Some(_) => !self.no_dev_txs,
603 None => {
604 if self.no_dev_txs {
606 eprintln!("The '--no-dev-txs' flag is ignored because '--dev' is not set");
607 }
608 false
609 }
610 };
611
612 match node_type {
614 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,
615 NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, storage_mode, shutdown.clone()).await,
616 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,
617 }
618 }
619
620 fn runtime() -> Runtime {
622 let num_cores = num_cpus::get();
624
625 let (num_tokio_worker_threads, max_tokio_blocking_threads, num_rayon_cores_global) =
629 (2 * num_cores, 512, num_cores);
630
631 rayon::ThreadPoolBuilder::new()
633 .stack_size(8 * 1024 * 1024)
634 .num_threads(num_rayon_cores_global)
635 .build_global()
636 .unwrap();
637
638 runtime::Builder::new_multi_thread()
640 .enable_all()
641 .thread_stack_size(8 * 1024 * 1024)
642 .worker_threads(num_tokio_worker_threads)
643 .max_blocking_threads(max_tokio_blocking_threads)
644 .build()
645 .expect("Failed to initialize a runtime for the router")
646 }
647}
648
649fn check_permissions(path: &PathBuf) -> Result<(), snarkvm::prelude::Error> {
650 #[cfg(target_family = "unix")]
651 {
652 use std::os::unix::fs::PermissionsExt;
653 ensure!(path.exists(), "The file '{:?}' does not exist", path);
654 crate::check_parent_permissions(path)?;
655 let permissions = path.metadata()?.permissions().mode();
656 ensure!(permissions & 0o777 == 0o600, "The file {:?} must be readable only by the owner (0600)", path);
657 }
658 Ok(())
659}
660
661fn load_or_compute_genesis<N: Network>(
663 genesis_private_key: PrivateKey<N>,
664 committee: Committee<N>,
665 public_balances: indexmap::IndexMap<Address<N>, u64>,
666 bonded_balances: indexmap::IndexMap<Address<N>, (Address<N>, Address<N>, u64)>,
667 dev_id: Option<u16>,
668 rng: &mut ChaChaRng,
669) -> Result<Block<N>> {
670 let mut preimage = Vec::new();
672
673 preimage.extend(&N::ID.to_le_bytes());
675 preimage.extend(&to_bytes_le![N::GENESIS_COINBASE_TARGET]?);
677 preimage.extend(&to_bytes_le![N::GENESIS_PROOF_TARGET]?);
679
680 preimage.extend(genesis_private_key.to_bytes_le()?);
682 preimage.extend(committee.to_bytes_le()?);
683 preimage.extend(&to_bytes_le![public_balances.iter().collect::<Vec<(_, _)>>()]?);
684 preimage.extend(&to_bytes_le![
685 bonded_balances
686 .iter()
687 .flat_map(|(staker, (validator, withdrawal, amount))| to_bytes_le![staker, validator, withdrawal, amount])
688 .collect::<Vec<_>>()
689 ]?);
690
691 match N::ID {
693 snarkvm::console::network::MainnetV0::ID => {
694 preimage.extend(snarkvm::parameters::mainnet::BondValidatorVerifier::METADATA.as_bytes());
695 preimage.extend(snarkvm::parameters::mainnet::BondPublicVerifier::METADATA.as_bytes());
696 preimage.extend(snarkvm::parameters::mainnet::UnbondPublicVerifier::METADATA.as_bytes());
697 preimage.extend(snarkvm::parameters::mainnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
698 preimage.extend(snarkvm::parameters::mainnet::SetValidatorStateVerifier::METADATA.as_bytes());
699 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateVerifier::METADATA.as_bytes());
700 preimage.extend(snarkvm::parameters::mainnet::TransferPublicVerifier::METADATA.as_bytes());
701 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
702 preimage.extend(snarkvm::parameters::mainnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
703 preimage.extend(snarkvm::parameters::mainnet::FeePrivateVerifier::METADATA.as_bytes());
704 preimage.extend(snarkvm::parameters::mainnet::FeePublicVerifier::METADATA.as_bytes());
705 preimage.extend(snarkvm::parameters::mainnet::InclusionVerifier::METADATA.as_bytes());
706 }
707 snarkvm::console::network::TestnetV0::ID => {
708 preimage.extend(snarkvm::parameters::testnet::BondValidatorVerifier::METADATA.as_bytes());
709 preimage.extend(snarkvm::parameters::testnet::BondPublicVerifier::METADATA.as_bytes());
710 preimage.extend(snarkvm::parameters::testnet::UnbondPublicVerifier::METADATA.as_bytes());
711 preimage.extend(snarkvm::parameters::testnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
712 preimage.extend(snarkvm::parameters::testnet::SetValidatorStateVerifier::METADATA.as_bytes());
713 preimage.extend(snarkvm::parameters::testnet::TransferPrivateVerifier::METADATA.as_bytes());
714 preimage.extend(snarkvm::parameters::testnet::TransferPublicVerifier::METADATA.as_bytes());
715 preimage.extend(snarkvm::parameters::testnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
716 preimage.extend(snarkvm::parameters::testnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
717 preimage.extend(snarkvm::parameters::testnet::FeePrivateVerifier::METADATA.as_bytes());
718 preimage.extend(snarkvm::parameters::testnet::FeePublicVerifier::METADATA.as_bytes());
719 preimage.extend(snarkvm::parameters::testnet::InclusionVerifier::METADATA.as_bytes());
720 }
721 snarkvm::console::network::CanaryV0::ID => {
722 preimage.extend(snarkvm::parameters::canary::BondValidatorVerifier::METADATA.as_bytes());
723 preimage.extend(snarkvm::parameters::canary::BondPublicVerifier::METADATA.as_bytes());
724 preimage.extend(snarkvm::parameters::canary::UnbondPublicVerifier::METADATA.as_bytes());
725 preimage.extend(snarkvm::parameters::canary::ClaimUnbondPublicVerifier::METADATA.as_bytes());
726 preimage.extend(snarkvm::parameters::canary::SetValidatorStateVerifier::METADATA.as_bytes());
727 preimage.extend(snarkvm::parameters::canary::TransferPrivateVerifier::METADATA.as_bytes());
728 preimage.extend(snarkvm::parameters::canary::TransferPublicVerifier::METADATA.as_bytes());
729 preimage.extend(snarkvm::parameters::canary::TransferPrivateToPublicVerifier::METADATA.as_bytes());
730 preimage.extend(snarkvm::parameters::canary::TransferPublicToPrivateVerifier::METADATA.as_bytes());
731 preimage.extend(snarkvm::parameters::canary::FeePrivateVerifier::METADATA.as_bytes());
732 preimage.extend(snarkvm::parameters::canary::FeePublicVerifier::METADATA.as_bytes());
733 preimage.extend(snarkvm::parameters::canary::InclusionVerifier::METADATA.as_bytes());
734 }
735 _ => {
736 bail!("Unrecognized Network ID: {}", N::ID);
738 }
739 }
740
741 let hasher = snarkvm::console::algorithms::BHP256::<N>::setup("aleo.dev.block")?;
743 let hash = hasher.hash(&preimage.to_bits_le())?.to_string();
747
748 let load_block = |file_path| -> Result<Block<N>> {
750 let buffer = std::fs::read(file_path)?;
752 Block::from_bytes_le(&buffer)
754 };
755
756 let file_path = std::env::temp_dir().join(hash);
758 if file_path.exists() {
760 if let Ok(block) = load_block(&file_path) {
762 return Ok(block);
763 }
764 }
765
766 let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(dev_id)?)?;
770 let block = vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng)?;
772 std::fs::write(&file_path, block.to_bytes_le()?)?;
774 Ok(block)
776}
777
778#[cfg(test)]
779mod tests {
780 use super::*;
781 use crate::commands::{CLI, Command};
782 use snarkvm::prelude::MainnetV0;
783
784 type CurrentNetwork = MainnetV0;
785
786 #[test]
787 fn test_parse_trusted_peers() {
788 let config = Start::try_parse_from(["snarkos", "--peers", ""].iter()).unwrap();
789 assert!(config.parse_trusted_peers().is_ok());
790 assert!(config.parse_trusted_peers().unwrap().is_empty());
791
792 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5"].iter()).unwrap();
793 assert!(config.parse_trusted_peers().is_ok());
794 assert_eq!(config.parse_trusted_peers().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
795
796 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
797 assert!(config.parse_trusted_peers().is_ok());
798 assert_eq!(config.parse_trusted_peers().unwrap(), vec![
799 SocketAddr::from_str("1.2.3.4:5").unwrap(),
800 SocketAddr::from_str("6.7.8.9:0").unwrap()
801 ]);
802 }
803
804 #[test]
805 fn test_parse_trusted_validators() {
806 let config = Start::try_parse_from(["snarkos", "--validators", ""].iter()).unwrap();
807 assert!(config.parse_trusted_validators().is_ok());
808 assert!(config.parse_trusted_validators().unwrap().is_empty());
809
810 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5"].iter()).unwrap();
811 assert!(config.parse_trusted_validators().is_ok());
812 assert_eq!(config.parse_trusted_validators().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
813
814 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
815 assert!(config.parse_trusted_validators().is_ok());
816 assert_eq!(config.parse_trusted_validators().unwrap(), vec![
817 SocketAddr::from_str("1.2.3.4:5").unwrap(),
818 SocketAddr::from_str("6.7.8.9:0").unwrap()
819 ]);
820 }
821
822 #[test]
823 fn test_parse_cdn() {
824 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
826 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
827 let config =
828 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter())
829 .unwrap();
830 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
831 let config =
832 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
833 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
834
835 let config =
837 Start::try_parse_from(["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
838 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
839 let config = Start::try_parse_from(
840 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
841 )
842 .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", ""].iter(),
846 )
847 .unwrap();
848 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
849
850 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
852 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
853 let config =
854 Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
855 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
856 let config =
857 Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
858 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
859
860 let config =
862 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
863 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
864 let config = Start::try_parse_from(
865 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
866 )
867 .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", ""].iter(),
871 )
872 .unwrap();
873 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
874
875 let config = Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
877 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
878 let config =
879 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
880 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
881 let config =
882 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
883 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
884
885 let config =
887 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
888 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
889 let config = Start::try_parse_from(
890 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
891 )
892 .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", ""].iter(),
896 )
897 .unwrap();
898 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
899
900 let config = Start::try_parse_from(["snarkos"].iter()).unwrap();
902 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
903 let config = Start::try_parse_from(["snarkos", "--cdn", "url"].iter()).unwrap();
904 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
905 let config = Start::try_parse_from(["snarkos", "--cdn", ""].iter()).unwrap();
906 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
907
908 let config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
910 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
911 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", "url"].iter()).unwrap();
912 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
913 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", ""].iter()).unwrap();
914 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
915 }
916
917 #[test]
918 fn test_parse_development_and_genesis() {
919 let prod_genesis = Block::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap();
920
921 let mut trusted_peers = vec![];
922 let mut trusted_validators = vec![];
923 let mut config = Start::try_parse_from(["snarkos"].iter()).unwrap();
924 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
925 let candidate_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
926 assert_eq!(trusted_peers.len(), 0);
927 assert_eq!(trusted_validators.len(), 0);
928 assert_eq!(candidate_genesis, prod_genesis);
929
930 let _config = Start::try_parse_from(["snarkos", "--dev", ""].iter()).unwrap_err();
931
932 let mut trusted_peers = vec![];
933 let mut trusted_validators = vec![];
934 let mut config = Start::try_parse_from(["snarkos", "--dev", "1"].iter()).unwrap();
935 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
936 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
937
938 let mut trusted_peers = vec![];
939 let mut trusted_validators = vec![];
940 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--rest", "127.0.0.1:8080"].iter()).unwrap();
941 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
942 assert_eq!(config.rest, Some(SocketAddr::from_str("127.0.0.1:8080").unwrap()));
943
944 let mut trusted_peers = vec![];
945 let mut trusted_validators = vec![];
946 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--norest"].iter()).unwrap();
947 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
948 assert!(config.rest.is_none());
949
950 let mut trusted_peers = vec![];
951 let mut trusted_validators = vec![];
952 let mut config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
953 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
954 let expected_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
955 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4130").unwrap()));
956 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3030").unwrap()));
957 assert_eq!(trusted_peers.len(), 0);
958 assert_eq!(trusted_validators.len(), 1);
959 assert!(!config.validator);
960 assert!(!config.prover);
961 assert!(!config.client);
962 assert_ne!(expected_genesis, prod_genesis);
963
964 let mut trusted_peers = vec![];
965 let mut trusted_validators = vec![];
966 let mut config =
967 Start::try_parse_from(["snarkos", "--dev", "1", "--validator", "--private-key", ""].iter()).unwrap();
968 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
969 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
970 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4131").unwrap()));
971 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
972 assert_eq!(trusted_peers.len(), 1);
973 assert_eq!(trusted_validators.len(), 1);
974 assert!(config.validator);
975 assert!(!config.prover);
976 assert!(!config.client);
977 assert_eq!(genesis, expected_genesis);
978
979 let mut trusted_peers = vec![];
980 let mut trusted_validators = vec![];
981 let mut config =
982 Start::try_parse_from(["snarkos", "--dev", "2", "--prover", "--private-key", ""].iter()).unwrap();
983 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
984 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
985 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4132").unwrap()));
986 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3032").unwrap()));
987 assert_eq!(trusted_peers.len(), 2);
988 assert_eq!(trusted_validators.len(), 2);
989 assert!(!config.validator);
990 assert!(config.prover);
991 assert!(!config.client);
992 assert_eq!(genesis, expected_genesis);
993
994 let mut trusted_peers = vec![];
995 let mut trusted_validators = vec![];
996 let mut config =
997 Start::try_parse_from(["snarkos", "--dev", "3", "--client", "--private-key", ""].iter()).unwrap();
998 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
999 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1000 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4133").unwrap()));
1001 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3033").unwrap()));
1002 assert_eq!(trusted_peers.len(), 3);
1003 assert_eq!(trusted_validators.len(), 2);
1004 assert!(!config.validator);
1005 assert!(!config.prover);
1006 assert!(config.client);
1007 assert_eq!(genesis, expected_genesis);
1008 }
1009
1010 #[test]
1011 fn clap_snarkos_start() {
1012 let arg_vec = vec![
1013 "snarkos",
1014 "start",
1015 "--nodisplay",
1016 "--dev",
1017 "2",
1018 "--validator",
1019 "--private-key",
1020 "PRIVATE_KEY",
1021 "--cdn",
1022 "CDN",
1023 "--peers",
1024 "IP1,IP2,IP3",
1025 "--validators",
1026 "IP1,IP2,IP3",
1027 "--rest",
1028 "127.0.0.1:3030",
1029 ];
1030 let cli = CLI::parse_from(arg_vec);
1031
1032 if let Command::Start(start) = cli.command {
1033 assert!(start.nodisplay);
1034 assert_eq!(start.dev, Some(2));
1035 assert!(start.validator);
1036 assert_eq!(start.private_key.as_deref(), Some("PRIVATE_KEY"));
1037 assert_eq!(start.cdn, Some("CDN".to_string()));
1038 assert_eq!(start.rest, Some("127.0.0.1:3030".parse().unwrap()));
1039 assert_eq!(start.network, 0);
1040 assert_eq!(start.peers, "IP1,IP2,IP3");
1041 assert_eq!(start.validators, "IP1,IP2,IP3");
1042 } else {
1043 panic!("Unexpected result of clap parsing!");
1044 }
1045 }
1046}