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")]
151 pub storage: Option<PathBuf>,
152 #[clap(long = "cdn")]
154 pub cdn: Option<String>,
155 #[clap(long)]
157 pub nocdn: bool,
158
159 #[clap(long)]
161 pub dev: Option<u16>,
162 #[clap(long)]
164 pub dev_num_validators: Option<u16>,
165 #[clap(default_value = "false", long = "no-dev-txs")]
167 pub no_dev_txs: bool,
168 #[clap(long)]
170 pub dev_bonded_balances: Option<BondedBalances>,
171}
172
173impl Start {
174 pub fn parse(self) -> Result<String> {
176 let shutdown: Arc<AtomicBool> = Default::default();
178
179 let log_receiver =
181 crate::helpers::initialize_logger(self.verbosity, self.nodisplay, self.logfile.clone(), shutdown.clone());
182 Self::runtime().block_on(async move {
184 let mut cli = self.clone();
186 match cli.network {
188 MainnetV0::ID => {
189 let node = cli.parse_node::<MainnetV0>(shutdown.clone()).await.expect("Failed to parse the node");
191 if !cli.nodisplay {
193 Display::start(node, log_receiver).expect("Failed to initialize the display");
195 }
196 }
197 TestnetV0::ID => {
198 let node = cli.parse_node::<TestnetV0>(shutdown.clone()).await.expect("Failed to parse the node");
200 if !cli.nodisplay {
202 Display::start(node, log_receiver).expect("Failed to initialize the display");
204 }
205 }
206 CanaryV0::ID => {
207 let node = cli.parse_node::<CanaryV0>(shutdown.clone()).await.expect("Failed to parse the node");
209 if !cli.nodisplay {
211 Display::start(node, log_receiver).expect("Failed to initialize the display");
213 }
214 }
215 _ => panic!("Invalid network ID specified"),
216 };
217 std::future::pending::<()>().await;
220 });
221
222 Ok(String::new())
223 }
224}
225
226impl Start {
227 fn parse_trusted_peers(&self) -> Result<Vec<SocketAddr>> {
229 match self.peers.is_empty() {
230 true => Ok(vec![]),
231 false => Ok(self
232 .peers
233 .split(',')
234 .flat_map(|ip| match ip.parse::<SocketAddr>() {
235 Ok(ip) => Some(ip),
236 Err(e) => {
237 eprintln!("The IP supplied to --peers ('{ip}') is malformed: {e}");
238 None
239 }
240 })
241 .collect()),
242 }
243 }
244
245 fn parse_trusted_validators(&self) -> Result<Vec<SocketAddr>> {
247 match self.validators.is_empty() {
248 true => Ok(vec![]),
249 false => Ok(self
250 .validators
251 .split(',')
252 .flat_map(|ip| match ip.parse::<SocketAddr>() {
253 Ok(ip) => Some(ip),
254 Err(e) => {
255 eprintln!("The IP supplied to --validators ('{ip}') is malformed: {e}");
256 None
257 }
258 })
259 .collect()),
260 }
261 }
262
263 fn parse_cdn<N: Network>(&self) -> Option<String> {
265 let is_no_node_type = !(self.validator || self.prover || self.client);
267
268 if self.dev.is_some() || self.nocdn || self.prover || is_no_node_type {
274 None
275 }
276 else {
278 match &self.cdn {
280 Some(cdn) => match cdn.is_empty() {
282 true => None,
283 false => Some(cdn.clone()),
284 },
285 None => match N::ID {
287 MainnetV0::ID => Some(format!("{CDN_BASE_URL}/v0/blocks/mainnet")),
288 TestnetV0::ID => Some(format!("{CDN_BASE_URL}/v0/blocks/testnet")),
289 CanaryV0::ID => Some(format!("{CDN_BASE_URL}/v0/blocks/canary")),
290 _ => None,
291 },
292 }
293 }
294 }
295
296 fn parse_private_key<N: Network>(&self) -> Result<Account<N>> {
299 match self.dev {
300 None => match (&self.private_key, &self.private_key_file) {
301 (Some(private_key), None) => Account::from_str(private_key.trim()),
303 (None, Some(path)) => {
305 check_permissions(path)?;
306 Account::from_str(std::fs::read_to_string(path)?.trim())
307 }
308 (None, None) => match self.client {
310 true => Account::new(&mut rand::thread_rng()),
311 false => bail!("Missing the '--private-key' or '--private-key-file' argument"),
312 },
313 (Some(_), Some(_)) => {
315 bail!("Cannot use '--private-key' and '--private-key-file' simultaneously, please use only one")
316 }
317 },
318 Some(dev) => {
319 Account::try_from({
321 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
323 for _ in 0..dev {
325 let _ = PrivateKey::<N>::new(&mut rng)?;
326 }
327 let private_key = PrivateKey::<N>::new(&mut rng)?;
328 println!("🔑 Your development private key for node {dev} is {}.\n", private_key.to_string().bold());
329 private_key
330 })
331 }
332 }
333 }
334
335 fn parse_development(
337 &mut self,
338 trusted_peers: &mut Vec<SocketAddr>,
339 trusted_validators: &mut Vec<SocketAddr>,
340 ) -> Result<()> {
341 if let Some(dev) = self.dev {
345 if trusted_peers.is_empty() {
347 for i in 0..dev {
348 trusted_peers.push(SocketAddr::from_str(&format!("127.0.0.1:{}", 4130 + i))?);
349 }
350 }
351 if trusted_validators.is_empty() {
353 for i in 0..2 {
355 if i != dev {
356 trusted_validators.push(SocketAddr::from_str(&format!("127.0.0.1:{}", MEMORY_POOL_PORT + i))?);
357 }
358 }
359 }
360 if self.node.is_none() {
364 self.node = Some(SocketAddr::from_str(&format!("0.0.0.0:{}", 4130 + dev))?);
365 }
366
367 if !self.norest && self.rest.is_none() {
369 self.rest = Some(SocketAddr::from_str(&format!("0.0.0.0:{}", 3030 + dev)).unwrap());
370 }
371 }
372 Ok(())
373 }
374
375 fn parse_genesis<N: Network>(&self) -> Result<Block<N>> {
378 if self.dev.is_some() {
379 let num_committee_members = match self.dev_num_validators {
381 Some(num_committee_members) => num_committee_members,
382 None => DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
383 };
384 ensure!(
385 num_committee_members >= DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
386 "Number of genesis committee members is too low"
387 );
388
389 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
391 let dev_keys =
393 (0..num_committee_members).map(|_| PrivateKey::<N>::new(&mut rng)).collect::<Result<Vec<_>>>()?;
394 let development_addresses = dev_keys.iter().map(Address::<N>::try_from).collect::<Result<Vec<_>>>()?;
396
397 let (committee, bonded_balances) = match &self.dev_bonded_balances {
399 Some(bonded_balances) => {
400 let bonded_balances = bonded_balances
402 .0
403 .iter()
404 .map(|(staker_address, (validator_address, withdrawal_address, amount))| {
405 let staker_addr = Address::<N>::from_str(staker_address)?;
406 let validator_addr = Address::<N>::from_str(validator_address)?;
407 let withdrawal_addr = Address::<N>::from_str(withdrawal_address)?;
408 Ok((staker_addr, (validator_addr, withdrawal_addr, *amount)))
409 })
410 .collect::<Result<IndexMap<_, _>>>()?;
411
412 let mut members = IndexMap::new();
414 for (staker_address, (validator_address, _, amount)) in bonded_balances.iter() {
415 match staker_address == validator_address {
417 true => ensure!(amount >= &MIN_VALIDATOR_STAKE, "Validator stake is too low"),
418 false => ensure!(amount >= &MIN_DELEGATOR_STAKE, "Delegator stake is too low"),
419 }
420
421 ensure!(
423 development_addresses.contains(validator_address),
424 "Validator address {validator_address} is not included in the list of development addresses"
425 );
426
427 members.entry(*validator_address).and_modify(|(stake, _, _)| *stake += amount).or_insert((
429 *amount,
430 true,
431 rng.gen_range(0..100),
432 ));
433 }
434 let committee = Committee::<N>::new(0u64, members)?;
436 (committee, bonded_balances)
437 }
438 None => {
439 let stake_per_member =
441 N::STARTING_SUPPLY.saturating_div(2).saturating_div(num_committee_members as u64);
442 ensure!(stake_per_member >= MIN_VALIDATOR_STAKE, "Committee stake per member is too low");
443
444 let members = development_addresses
446 .iter()
447 .map(|address| (*address, (stake_per_member, true, rng.gen_range(0..100))))
448 .collect::<IndexMap<_, _>>();
449
450 let bonded_balances = members
453 .iter()
454 .map(|(address, (stake, _, _))| (*address, (*address, *address, *stake)))
455 .collect::<IndexMap<_, _>>();
456 let committee = Committee::<N>::new(0u64, members)?;
458
459 (committee, bonded_balances)
460 }
461 };
462
463 ensure!(
465 committee.members().len() == num_committee_members as usize,
466 "Number of committee members {} does not match the expected number of members {num_committee_members}",
467 committee.members().len()
468 );
469
470 let remaining_balance = N::STARTING_SUPPLY.saturating_sub(committee.total_stake());
472 let public_balance_per_validator = remaining_balance.saturating_div(num_committee_members as u64);
473
474 let mut public_balances = dev_keys
476 .iter()
477 .map(|private_key| Ok((Address::try_from(private_key)?, public_balance_per_validator)))
478 .collect::<Result<indexmap::IndexMap<_, _>>>()?;
479
480 let leftover =
482 remaining_balance.saturating_sub(public_balance_per_validator * num_committee_members as u64);
483 if leftover > 0 {
484 let (_, balance) = public_balances.get_index_mut(0).unwrap();
485 *balance += leftover;
486 }
487
488 let public_balances_sum: u64 = public_balances.values().copied().sum();
490 if committee.total_stake() + public_balances_sum != N::STARTING_SUPPLY {
491 bail!("Sum of committee stakes and public balances does not equal total starting supply.");
492 }
493
494 load_or_compute_genesis(dev_keys[0], committee, public_balances, bonded_balances, &mut rng)
496 } else {
497 if self.dev_num_validators.is_some() {
499 eprintln!("The '--dev-num-validators' flag is ignored because '--dev' is not set");
500 }
501
502 Block::from_bytes_le(N::genesis_bytes())
503 }
504 }
505
506 const fn parse_node_type(&self) -> NodeType {
508 if self.validator {
509 NodeType::Validator
510 } else if self.prover {
511 NodeType::Prover
512 } else {
513 NodeType::Client
514 }
515 }
516
517 #[rustfmt::skip]
519 async fn parse_node<N: Network>(&mut self, shutdown: Arc<AtomicBool>) -> Result<Node<N>> {
520 println!("{}", crate::helpers::welcome_message());
522
523 if cfg!(feature = "test_targets") && self.dev.is_none() {
526 bail!("The 'test_targets' feature is enabled, but the '--dev' flag is not set");
527 }
528
529 let mut trusted_peers = self.parse_trusted_peers()?;
531 let mut trusted_validators = self.parse_trusted_validators()?;
533 self.parse_development(&mut trusted_peers, &mut trusted_validators)?;
535
536 let cdn = self.parse_cdn::<N>();
538
539 let genesis = self.parse_genesis::<N>()?;
541 let account = self.parse_private_key::<N>()?;
543 let node_type = self.parse_node_type();
545
546 let node_ip = match self.node {
548 Some(node_ip) => node_ip,
549 None => SocketAddr::from_str("0.0.0.0:4130").unwrap(),
550 };
551
552 let rest_ip = match self.norest {
554 true => None,
555 false => self.rest.or_else(|| Some("0.0.0.0:3030".parse().unwrap())),
556 };
557
558 if self.nodisplay {
560 println!("👛 Your Aleo address is {}.\n", account.address().to_string().bold());
562 println!(
564 "🧭 Starting {} on {} at {}.\n",
565 node_type.description().bold(),
566 N::NAME.bold(),
567 node_ip.to_string().bold()
568 );
569
570 if node_type.is_validator() {
572 if let Some(rest_ip) = rest_ip {
573 println!("🌐 Starting the REST server at {}.\n", rest_ip.to_string().bold());
574
575 if let Ok(jwt_token) = snarkos_node_rest::Claims::new(account.address()).to_jwt_string() {
576 println!("🔑 Your one-time JWT token is {}\n", jwt_token.dimmed());
577 }
578 }
579 }
580 }
581
582 #[cfg(target_family = "unix")]
584 if node_type.is_validator() {
585 crate::helpers::check_open_files_limit(RECOMMENDED_MIN_NOFILES_LIMIT);
586 }
587 crate::helpers::check_validator_machine(node_type);
589
590 #[cfg(feature = "metrics")]
592 if self.metrics {
593 metrics::initialize_metrics(self.metrics_ip);
594 }
595
596 let storage_mode = match &self.storage {
598 Some(path) => StorageMode::Custom(path.clone()),
599 None => match self.dev {
600 Some(id) => StorageMode::Development(id),
601 None => StorageMode::Production,
602 },
603 };
604
605 let dev_txs = match self.dev {
607 Some(_) => !self.no_dev_txs,
608 None => {
609 if self.no_dev_txs {
611 eprintln!("The '--no-dev-txs' flag is ignored because '--dev' is not set");
612 }
613 false
614 }
615 };
616
617 match node_type {
619 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,
620 NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, storage_mode, shutdown.clone()).await,
621 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,
622 }
623 }
624
625 fn runtime() -> Runtime {
627 let num_cores = num_cpus::get();
629
630 let (num_tokio_worker_threads, max_tokio_blocking_threads, num_rayon_cores_global) =
634 (2 * num_cores, 512, num_cores);
635
636 rayon::ThreadPoolBuilder::new()
638 .stack_size(8 * 1024 * 1024)
639 .num_threads(num_rayon_cores_global)
640 .build_global()
641 .unwrap();
642
643 runtime::Builder::new_multi_thread()
645 .enable_all()
646 .thread_stack_size(8 * 1024 * 1024)
647 .worker_threads(num_tokio_worker_threads)
648 .max_blocking_threads(max_tokio_blocking_threads)
649 .build()
650 .expect("Failed to initialize a runtime for the router")
651 }
652}
653
654fn check_permissions(path: &PathBuf) -> Result<(), snarkvm::prelude::Error> {
655 #[cfg(target_family = "unix")]
656 {
657 use std::os::unix::fs::PermissionsExt;
658 ensure!(path.exists(), "The file '{:?}' does not exist", path);
659 crate::check_parent_permissions(path)?;
660 let permissions = path.metadata()?.permissions().mode();
661 ensure!(permissions & 0o777 == 0o600, "The file {:?} must be readable only by the owner (0600)", path);
662 }
663 Ok(())
664}
665
666fn load_or_compute_genesis<N: Network>(
668 genesis_private_key: PrivateKey<N>,
669 committee: Committee<N>,
670 public_balances: indexmap::IndexMap<Address<N>, u64>,
671 bonded_balances: indexmap::IndexMap<Address<N>, (Address<N>, Address<N>, u64)>,
672 rng: &mut ChaChaRng,
673) -> Result<Block<N>> {
674 let mut preimage = Vec::new();
676
677 preimage.extend(&N::ID.to_le_bytes());
679 preimage.extend(&to_bytes_le![N::GENESIS_COINBASE_TARGET]?);
681 preimage.extend(&to_bytes_le![N::GENESIS_PROOF_TARGET]?);
683
684 preimage.extend(genesis_private_key.to_bytes_le()?);
686 preimage.extend(committee.to_bytes_le()?);
687 preimage.extend(&to_bytes_le![public_balances.iter().collect::<Vec<(_, _)>>()]?);
688 preimage.extend(&to_bytes_le![
689 bonded_balances
690 .iter()
691 .flat_map(|(staker, (validator, withdrawal, amount))| to_bytes_le![staker, validator, withdrawal, amount])
692 .collect::<Vec<_>>()
693 ]?);
694
695 match N::ID {
697 snarkvm::console::network::MainnetV0::ID => {
698 preimage.extend(snarkvm::parameters::mainnet::BondValidatorVerifier::METADATA.as_bytes());
699 preimage.extend(snarkvm::parameters::mainnet::BondPublicVerifier::METADATA.as_bytes());
700 preimage.extend(snarkvm::parameters::mainnet::UnbondPublicVerifier::METADATA.as_bytes());
701 preimage.extend(snarkvm::parameters::mainnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
702 preimage.extend(snarkvm::parameters::mainnet::SetValidatorStateVerifier::METADATA.as_bytes());
703 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateVerifier::METADATA.as_bytes());
704 preimage.extend(snarkvm::parameters::mainnet::TransferPublicVerifier::METADATA.as_bytes());
705 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
706 preimage.extend(snarkvm::parameters::mainnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
707 preimage.extend(snarkvm::parameters::mainnet::FeePrivateVerifier::METADATA.as_bytes());
708 preimage.extend(snarkvm::parameters::mainnet::FeePublicVerifier::METADATA.as_bytes());
709 preimage.extend(snarkvm::parameters::mainnet::InclusionVerifier::METADATA.as_bytes());
710 }
711 snarkvm::console::network::TestnetV0::ID => {
712 preimage.extend(snarkvm::parameters::testnet::BondValidatorVerifier::METADATA.as_bytes());
713 preimage.extend(snarkvm::parameters::testnet::BondPublicVerifier::METADATA.as_bytes());
714 preimage.extend(snarkvm::parameters::testnet::UnbondPublicVerifier::METADATA.as_bytes());
715 preimage.extend(snarkvm::parameters::testnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
716 preimage.extend(snarkvm::parameters::testnet::SetValidatorStateVerifier::METADATA.as_bytes());
717 preimage.extend(snarkvm::parameters::testnet::TransferPrivateVerifier::METADATA.as_bytes());
718 preimage.extend(snarkvm::parameters::testnet::TransferPublicVerifier::METADATA.as_bytes());
719 preimage.extend(snarkvm::parameters::testnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
720 preimage.extend(snarkvm::parameters::testnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
721 preimage.extend(snarkvm::parameters::testnet::FeePrivateVerifier::METADATA.as_bytes());
722 preimage.extend(snarkvm::parameters::testnet::FeePublicVerifier::METADATA.as_bytes());
723 preimage.extend(snarkvm::parameters::testnet::InclusionVerifier::METADATA.as_bytes());
724 }
725 snarkvm::console::network::CanaryV0::ID => {
726 preimage.extend(snarkvm::parameters::canary::BondValidatorVerifier::METADATA.as_bytes());
727 preimage.extend(snarkvm::parameters::canary::BondPublicVerifier::METADATA.as_bytes());
728 preimage.extend(snarkvm::parameters::canary::UnbondPublicVerifier::METADATA.as_bytes());
729 preimage.extend(snarkvm::parameters::canary::ClaimUnbondPublicVerifier::METADATA.as_bytes());
730 preimage.extend(snarkvm::parameters::canary::SetValidatorStateVerifier::METADATA.as_bytes());
731 preimage.extend(snarkvm::parameters::canary::TransferPrivateVerifier::METADATA.as_bytes());
732 preimage.extend(snarkvm::parameters::canary::TransferPublicVerifier::METADATA.as_bytes());
733 preimage.extend(snarkvm::parameters::canary::TransferPrivateToPublicVerifier::METADATA.as_bytes());
734 preimage.extend(snarkvm::parameters::canary::TransferPublicToPrivateVerifier::METADATA.as_bytes());
735 preimage.extend(snarkvm::parameters::canary::FeePrivateVerifier::METADATA.as_bytes());
736 preimage.extend(snarkvm::parameters::canary::FeePublicVerifier::METADATA.as_bytes());
737 preimage.extend(snarkvm::parameters::canary::InclusionVerifier::METADATA.as_bytes());
738 }
739 _ => {
740 bail!("Unrecognized Network ID: {}", N::ID);
742 }
743 }
744
745 let hasher = snarkvm::console::algorithms::BHP256::<N>::setup("aleo.dev.block")?;
747 let hash = hasher.hash(&preimage.to_bits_le())?.to_string();
751
752 let load_block = |file_path| -> Result<Block<N>> {
754 let buffer = std::fs::read(file_path)?;
756 Block::from_bytes_le(&buffer)
758 };
759
760 let file_path = std::env::temp_dir().join(hash);
762 if file_path.exists() {
764 if let Ok(block) = load_block(&file_path) {
766 return Ok(block);
767 }
768 }
769
770 let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::new_test(None))?)?;
774 let block = vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng)?;
776 std::fs::write(&file_path, block.to_bytes_le()?)?;
778 Ok(block)
780}
781
782#[cfg(test)]
783mod tests {
784 use super::*;
785 use crate::commands::{CLI, Command};
786 use snarkvm::prelude::MainnetV0;
787
788 type CurrentNetwork = MainnetV0;
789
790 #[test]
791 fn test_parse_trusted_peers() {
792 let config = Start::try_parse_from(["snarkos", "--peers", ""].iter()).unwrap();
793 assert!(config.parse_trusted_peers().is_ok());
794 assert!(config.parse_trusted_peers().unwrap().is_empty());
795
796 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5"].iter()).unwrap();
797 assert!(config.parse_trusted_peers().is_ok());
798 assert_eq!(config.parse_trusted_peers().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
799
800 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
801 assert!(config.parse_trusted_peers().is_ok());
802 assert_eq!(config.parse_trusted_peers().unwrap(), vec![
803 SocketAddr::from_str("1.2.3.4:5").unwrap(),
804 SocketAddr::from_str("6.7.8.9:0").unwrap()
805 ]);
806 }
807
808 #[test]
809 fn test_parse_trusted_validators() {
810 let config = Start::try_parse_from(["snarkos", "--validators", ""].iter()).unwrap();
811 assert!(config.parse_trusted_validators().is_ok());
812 assert!(config.parse_trusted_validators().unwrap().is_empty());
813
814 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5"].iter()).unwrap();
815 assert!(config.parse_trusted_validators().is_ok());
816 assert_eq!(config.parse_trusted_validators().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
817
818 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
819 assert!(config.parse_trusted_validators().is_ok());
820 assert_eq!(config.parse_trusted_validators().unwrap(), vec![
821 SocketAddr::from_str("1.2.3.4:5").unwrap(),
822 SocketAddr::from_str("6.7.8.9:0").unwrap()
823 ]);
824 }
825
826 #[test]
827 fn test_parse_cdn() {
828 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
830 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
831 let config =
832 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter())
833 .unwrap();
834 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
835 let config =
836 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
837 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
838
839 let config =
841 Start::try_parse_from(["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
842 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
843 let config = Start::try_parse_from(
844 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
845 )
846 .unwrap();
847 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
848 let config = Start::try_parse_from(
849 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", ""].iter(),
850 )
851 .unwrap();
852 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
853
854 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
856 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
857 let config =
858 Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
859 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
860 let config =
861 Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
862 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
863
864 let config =
866 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
867 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
868 let config = Start::try_parse_from(
869 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
870 )
871 .unwrap();
872 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
873 let config = Start::try_parse_from(
874 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", ""].iter(),
875 )
876 .unwrap();
877 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
878
879 let config = Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
881 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
882 let config =
883 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
884 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
885 let config =
886 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
887 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
888
889 let config =
891 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
892 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
893 let config = Start::try_parse_from(
894 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
895 )
896 .unwrap();
897 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
898 let config = Start::try_parse_from(
899 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", ""].iter(),
900 )
901 .unwrap();
902 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
903
904 let config = Start::try_parse_from(["snarkos"].iter()).unwrap();
906 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
907 let config = Start::try_parse_from(["snarkos", "--cdn", "url"].iter()).unwrap();
908 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
909 let config = Start::try_parse_from(["snarkos", "--cdn", ""].iter()).unwrap();
910 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
911
912 let config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
914 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
915 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", "url"].iter()).unwrap();
916 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
917 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", ""].iter()).unwrap();
918 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
919 }
920
921 #[test]
922 fn test_parse_development_and_genesis() {
923 let prod_genesis = Block::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap();
924
925 let mut trusted_peers = vec![];
926 let mut trusted_validators = vec![];
927 let mut config = Start::try_parse_from(["snarkos"].iter()).unwrap();
928 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
929 let candidate_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
930 assert_eq!(trusted_peers.len(), 0);
931 assert_eq!(trusted_validators.len(), 0);
932 assert_eq!(candidate_genesis, prod_genesis);
933
934 let _config = Start::try_parse_from(["snarkos", "--dev", ""].iter()).unwrap_err();
935
936 let mut trusted_peers = vec![];
937 let mut trusted_validators = vec![];
938 let mut config = Start::try_parse_from(["snarkos", "--dev", "1"].iter()).unwrap();
939 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
940 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
941
942 let mut trusted_peers = vec![];
943 let mut trusted_validators = vec![];
944 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--rest", "127.0.0.1:8080"].iter()).unwrap();
945 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
946 assert_eq!(config.rest, Some(SocketAddr::from_str("127.0.0.1:8080").unwrap()));
947
948 let mut trusted_peers = vec![];
949 let mut trusted_validators = vec![];
950 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--norest"].iter()).unwrap();
951 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
952 assert!(config.rest.is_none());
953
954 let mut trusted_peers = vec![];
955 let mut trusted_validators = vec![];
956 let mut config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
957 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
958 let expected_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
959 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4130").unwrap()));
960 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3030").unwrap()));
961 assert_eq!(trusted_peers.len(), 0);
962 assert_eq!(trusted_validators.len(), 1);
963 assert!(!config.validator);
964 assert!(!config.prover);
965 assert!(!config.client);
966 assert_ne!(expected_genesis, prod_genesis);
967
968 let mut trusted_peers = vec![];
969 let mut trusted_validators = vec![];
970 let mut config =
971 Start::try_parse_from(["snarkos", "--dev", "1", "--validator", "--private-key", ""].iter()).unwrap();
972 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
973 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
974 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4131").unwrap()));
975 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
976 assert_eq!(trusted_peers.len(), 1);
977 assert_eq!(trusted_validators.len(), 1);
978 assert!(config.validator);
979 assert!(!config.prover);
980 assert!(!config.client);
981 assert_eq!(genesis, expected_genesis);
982
983 let mut trusted_peers = vec![];
984 let mut trusted_validators = vec![];
985 let mut config =
986 Start::try_parse_from(["snarkos", "--dev", "2", "--prover", "--private-key", ""].iter()).unwrap();
987 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
988 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
989 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4132").unwrap()));
990 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3032").unwrap()));
991 assert_eq!(trusted_peers.len(), 2);
992 assert_eq!(trusted_validators.len(), 2);
993 assert!(!config.validator);
994 assert!(config.prover);
995 assert!(!config.client);
996 assert_eq!(genesis, expected_genesis);
997
998 let mut trusted_peers = vec![];
999 let mut trusted_validators = vec![];
1000 let mut config =
1001 Start::try_parse_from(["snarkos", "--dev", "3", "--client", "--private-key", ""].iter()).unwrap();
1002 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1003 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1004 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4133").unwrap()));
1005 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3033").unwrap()));
1006 assert_eq!(trusted_peers.len(), 3);
1007 assert_eq!(trusted_validators.len(), 2);
1008 assert!(!config.validator);
1009 assert!(!config.prover);
1010 assert!(config.client);
1011 assert_eq!(genesis, expected_genesis);
1012 }
1013
1014 #[test]
1015 fn clap_snarkos_start() {
1016 let arg_vec = vec![
1017 "snarkos",
1018 "start",
1019 "--nodisplay",
1020 "--dev",
1021 "2",
1022 "--validator",
1023 "--private-key",
1024 "PRIVATE_KEY",
1025 "--cdn",
1026 "CDN",
1027 "--peers",
1028 "IP1,IP2,IP3",
1029 "--validators",
1030 "IP1,IP2,IP3",
1031 "--rest",
1032 "127.0.0.1:3030",
1033 ];
1034 let cli = CLI::parse_from(arg_vec);
1035
1036 if let Command::Start(start) = cli.command {
1037 assert!(start.nodisplay);
1038 assert_eq!(start.dev, Some(2));
1039 assert!(start.validator);
1040 assert_eq!(start.private_key.as_deref(), Some("PRIVATE_KEY"));
1041 assert_eq!(start.cdn, Some("CDN".to_string()));
1042 assert_eq!(start.rest, Some("127.0.0.1:3030".parse().unwrap()));
1043 assert_eq!(start.network, 0);
1044 assert_eq!(start.peers, "IP1,IP2,IP3");
1045 assert_eq!(start.validators, "IP1,IP2,IP3");
1046 } else {
1047 panic!("Unexpected result of clap parsing!");
1048 }
1049 }
1050}