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://blocks.aleo.org";
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}/mainnet/v0")),
288 TestnetV0::ID => Some(format!("{CDN_BASE_URL}/testnet/v0")),
289 CanaryV0::ID => Some(format!("{CDN_BASE_URL}/canary/v0")),
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, self.dev, &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 => StorageMode::from(self.dev),
600 };
601
602 let dev_txs = match self.dev {
604 Some(_) => !self.no_dev_txs,
605 None => {
606 if self.no_dev_txs {
608 eprintln!("The '--no-dev-txs' flag is ignored because '--dev' is not set");
609 }
610 false
611 }
612 };
613
614 match node_type {
616 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,
617 NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, storage_mode, shutdown.clone()).await,
618 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,
619 }
620 }
621
622 fn runtime() -> Runtime {
624 let num_cores = num_cpus::get();
626
627 let (num_tokio_worker_threads, max_tokio_blocking_threads, num_rayon_cores_global) =
631 (2 * num_cores, 512, num_cores);
632
633 rayon::ThreadPoolBuilder::new()
635 .stack_size(8 * 1024 * 1024)
636 .num_threads(num_rayon_cores_global)
637 .build_global()
638 .unwrap();
639
640 runtime::Builder::new_multi_thread()
642 .enable_all()
643 .thread_stack_size(8 * 1024 * 1024)
644 .worker_threads(num_tokio_worker_threads)
645 .max_blocking_threads(max_tokio_blocking_threads)
646 .build()
647 .expect("Failed to initialize a runtime for the router")
648 }
649}
650
651fn check_permissions(path: &PathBuf) -> Result<(), snarkvm::prelude::Error> {
652 #[cfg(target_family = "unix")]
653 {
654 use std::os::unix::fs::PermissionsExt;
655 ensure!(path.exists(), "The file '{:?}' does not exist", path);
656 crate::check_parent_permissions(path)?;
657 let permissions = path.metadata()?.permissions().mode();
658 ensure!(permissions & 0o777 == 0o600, "The file {:?} must be readable only by the owner (0600)", path);
659 }
660 Ok(())
661}
662
663fn load_or_compute_genesis<N: Network>(
665 genesis_private_key: PrivateKey<N>,
666 committee: Committee<N>,
667 public_balances: indexmap::IndexMap<Address<N>, u64>,
668 bonded_balances: indexmap::IndexMap<Address<N>, (Address<N>, Address<N>, u64)>,
669 dev_id: Option<u16>,
670 rng: &mut ChaChaRng,
671) -> Result<Block<N>> {
672 let mut preimage = Vec::new();
674
675 preimage.extend(&N::ID.to_le_bytes());
677 preimage.extend(&to_bytes_le![N::GENESIS_COINBASE_TARGET]?);
679 preimage.extend(&to_bytes_le![N::GENESIS_PROOF_TARGET]?);
681
682 preimage.extend(genesis_private_key.to_bytes_le()?);
684 preimage.extend(committee.to_bytes_le()?);
685 preimage.extend(&to_bytes_le![public_balances.iter().collect::<Vec<(_, _)>>()]?);
686 preimage.extend(&to_bytes_le![
687 bonded_balances
688 .iter()
689 .flat_map(|(staker, (validator, withdrawal, amount))| to_bytes_le![staker, validator, withdrawal, amount])
690 .collect::<Vec<_>>()
691 ]?);
692
693 match N::ID {
695 snarkvm::console::network::MainnetV0::ID => {
696 preimage.extend(snarkvm::parameters::mainnet::BondValidatorVerifier::METADATA.as_bytes());
697 preimage.extend(snarkvm::parameters::mainnet::BondPublicVerifier::METADATA.as_bytes());
698 preimage.extend(snarkvm::parameters::mainnet::UnbondPublicVerifier::METADATA.as_bytes());
699 preimage.extend(snarkvm::parameters::mainnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
700 preimage.extend(snarkvm::parameters::mainnet::SetValidatorStateVerifier::METADATA.as_bytes());
701 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateVerifier::METADATA.as_bytes());
702 preimage.extend(snarkvm::parameters::mainnet::TransferPublicVerifier::METADATA.as_bytes());
703 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
704 preimage.extend(snarkvm::parameters::mainnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
705 preimage.extend(snarkvm::parameters::mainnet::FeePrivateVerifier::METADATA.as_bytes());
706 preimage.extend(snarkvm::parameters::mainnet::FeePublicVerifier::METADATA.as_bytes());
707 preimage.extend(snarkvm::parameters::mainnet::InclusionVerifier::METADATA.as_bytes());
708 }
709 snarkvm::console::network::TestnetV0::ID => {
710 preimage.extend(snarkvm::parameters::testnet::BondValidatorVerifier::METADATA.as_bytes());
711 preimage.extend(snarkvm::parameters::testnet::BondPublicVerifier::METADATA.as_bytes());
712 preimage.extend(snarkvm::parameters::testnet::UnbondPublicVerifier::METADATA.as_bytes());
713 preimage.extend(snarkvm::parameters::testnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
714 preimage.extend(snarkvm::parameters::testnet::SetValidatorStateVerifier::METADATA.as_bytes());
715 preimage.extend(snarkvm::parameters::testnet::TransferPrivateVerifier::METADATA.as_bytes());
716 preimage.extend(snarkvm::parameters::testnet::TransferPublicVerifier::METADATA.as_bytes());
717 preimage.extend(snarkvm::parameters::testnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
718 preimage.extend(snarkvm::parameters::testnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
719 preimage.extend(snarkvm::parameters::testnet::FeePrivateVerifier::METADATA.as_bytes());
720 preimage.extend(snarkvm::parameters::testnet::FeePublicVerifier::METADATA.as_bytes());
721 preimage.extend(snarkvm::parameters::testnet::InclusionVerifier::METADATA.as_bytes());
722 }
723 snarkvm::console::network::CanaryV0::ID => {
724 preimage.extend(snarkvm::parameters::canary::BondValidatorVerifier::METADATA.as_bytes());
725 preimage.extend(snarkvm::parameters::canary::BondPublicVerifier::METADATA.as_bytes());
726 preimage.extend(snarkvm::parameters::canary::UnbondPublicVerifier::METADATA.as_bytes());
727 preimage.extend(snarkvm::parameters::canary::ClaimUnbondPublicVerifier::METADATA.as_bytes());
728 preimage.extend(snarkvm::parameters::canary::SetValidatorStateVerifier::METADATA.as_bytes());
729 preimage.extend(snarkvm::parameters::canary::TransferPrivateVerifier::METADATA.as_bytes());
730 preimage.extend(snarkvm::parameters::canary::TransferPublicVerifier::METADATA.as_bytes());
731 preimage.extend(snarkvm::parameters::canary::TransferPrivateToPublicVerifier::METADATA.as_bytes());
732 preimage.extend(snarkvm::parameters::canary::TransferPublicToPrivateVerifier::METADATA.as_bytes());
733 preimage.extend(snarkvm::parameters::canary::FeePrivateVerifier::METADATA.as_bytes());
734 preimage.extend(snarkvm::parameters::canary::FeePublicVerifier::METADATA.as_bytes());
735 preimage.extend(snarkvm::parameters::canary::InclusionVerifier::METADATA.as_bytes());
736 }
737 _ => {
738 bail!("Unrecognized Network ID: {}", N::ID);
740 }
741 }
742
743 let hasher = snarkvm::console::algorithms::BHP256::<N>::setup("aleo.dev.block")?;
745 let hash = hasher.hash(&preimage.to_bits_le())?.to_string();
749
750 let load_block = |file_path| -> Result<Block<N>> {
752 let buffer = std::fs::read(file_path)?;
754 Block::from_bytes_le(&buffer)
756 };
757
758 let file_path = std::env::temp_dir().join(hash);
760 if file_path.exists() {
762 if let Ok(block) = load_block(&file_path) {
764 return Ok(block);
765 }
766 }
767
768 let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(dev_id)?)?;
772 let block = vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng)?;
774 std::fs::write(&file_path, block.to_bytes_le()?)?;
776 Ok(block)
778}
779
780#[cfg(test)]
781mod tests {
782 use super::*;
783 use crate::commands::{CLI, Command};
784 use snarkvm::prelude::MainnetV0;
785
786 type CurrentNetwork = MainnetV0;
787
788 #[test]
789 fn test_parse_trusted_peers() {
790 let config = Start::try_parse_from(["snarkos", "--peers", ""].iter()).unwrap();
791 assert!(config.parse_trusted_peers().is_ok());
792 assert!(config.parse_trusted_peers().unwrap().is_empty());
793
794 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5"].iter()).unwrap();
795 assert!(config.parse_trusted_peers().is_ok());
796 assert_eq!(config.parse_trusted_peers().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
797
798 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
799 assert!(config.parse_trusted_peers().is_ok());
800 assert_eq!(config.parse_trusted_peers().unwrap(), vec![
801 SocketAddr::from_str("1.2.3.4:5").unwrap(),
802 SocketAddr::from_str("6.7.8.9:0").unwrap()
803 ]);
804 }
805
806 #[test]
807 fn test_parse_trusted_validators() {
808 let config = Start::try_parse_from(["snarkos", "--validators", ""].iter()).unwrap();
809 assert!(config.parse_trusted_validators().is_ok());
810 assert!(config.parse_trusted_validators().unwrap().is_empty());
811
812 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5"].iter()).unwrap();
813 assert!(config.parse_trusted_validators().is_ok());
814 assert_eq!(config.parse_trusted_validators().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
815
816 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
817 assert!(config.parse_trusted_validators().is_ok());
818 assert_eq!(config.parse_trusted_validators().unwrap(), vec![
819 SocketAddr::from_str("1.2.3.4:5").unwrap(),
820 SocketAddr::from_str("6.7.8.9:0").unwrap()
821 ]);
822 }
823
824 #[test]
825 fn test_parse_cdn() {
826 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
828 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
829 let config =
830 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter())
831 .unwrap();
832 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
833 let config =
834 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
835 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
836
837 let config =
839 Start::try_parse_from(["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
840 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
841 let config = Start::try_parse_from(
842 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
843 )
844 .unwrap();
845 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
846 let config = Start::try_parse_from(
847 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", ""].iter(),
848 )
849 .unwrap();
850 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
851
852 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
854 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
855 let config =
856 Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
857 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
858 let config =
859 Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
860 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
861
862 let config =
864 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
865 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
866 let config = Start::try_parse_from(
867 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
868 )
869 .unwrap();
870 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
871 let config = Start::try_parse_from(
872 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", ""].iter(),
873 )
874 .unwrap();
875 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
876
877 let config = Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
879 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
880 let config =
881 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
882 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
883 let config =
884 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
885 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
886
887 let config =
889 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
890 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
891 let config = Start::try_parse_from(
892 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
893 )
894 .unwrap();
895 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
896 let config = Start::try_parse_from(
897 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", ""].iter(),
898 )
899 .unwrap();
900 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
901
902 let config = Start::try_parse_from(["snarkos"].iter()).unwrap();
904 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
905 let config = Start::try_parse_from(["snarkos", "--cdn", "url"].iter()).unwrap();
906 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
907 let config = Start::try_parse_from(["snarkos", "--cdn", ""].iter()).unwrap();
908 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
909
910 let config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
912 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
913 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", "url"].iter()).unwrap();
914 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
915 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", ""].iter()).unwrap();
916 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
917 }
918
919 #[test]
920 fn test_parse_development_and_genesis() {
921 let prod_genesis = Block::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap();
922
923 let mut trusted_peers = vec![];
924 let mut trusted_validators = vec![];
925 let mut config = Start::try_parse_from(["snarkos"].iter()).unwrap();
926 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
927 let candidate_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
928 assert_eq!(trusted_peers.len(), 0);
929 assert_eq!(trusted_validators.len(), 0);
930 assert_eq!(candidate_genesis, prod_genesis);
931
932 let _config = Start::try_parse_from(["snarkos", "--dev", ""].iter()).unwrap_err();
933
934 let mut trusted_peers = vec![];
935 let mut trusted_validators = vec![];
936 let mut config = Start::try_parse_from(["snarkos", "--dev", "1"].iter()).unwrap();
937 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
938 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
939
940 let mut trusted_peers = vec![];
941 let mut trusted_validators = vec![];
942 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--rest", "127.0.0.1:8080"].iter()).unwrap();
943 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
944 assert_eq!(config.rest, Some(SocketAddr::from_str("127.0.0.1:8080").unwrap()));
945
946 let mut trusted_peers = vec![];
947 let mut trusted_validators = vec![];
948 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--norest"].iter()).unwrap();
949 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
950 assert!(config.rest.is_none());
951
952 let mut trusted_peers = vec![];
953 let mut trusted_validators = vec![];
954 let mut config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
955 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
956 let expected_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
957 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4130").unwrap()));
958 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3030").unwrap()));
959 assert_eq!(trusted_peers.len(), 0);
960 assert_eq!(trusted_validators.len(), 1);
961 assert!(!config.validator);
962 assert!(!config.prover);
963 assert!(!config.client);
964 assert_ne!(expected_genesis, prod_genesis);
965
966 let mut trusted_peers = vec![];
967 let mut trusted_validators = vec![];
968 let mut config =
969 Start::try_parse_from(["snarkos", "--dev", "1", "--validator", "--private-key", ""].iter()).unwrap();
970 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
971 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
972 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4131").unwrap()));
973 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
974 assert_eq!(trusted_peers.len(), 1);
975 assert_eq!(trusted_validators.len(), 1);
976 assert!(config.validator);
977 assert!(!config.prover);
978 assert!(!config.client);
979 assert_eq!(genesis, expected_genesis);
980
981 let mut trusted_peers = vec![];
982 let mut trusted_validators = vec![];
983 let mut config =
984 Start::try_parse_from(["snarkos", "--dev", "2", "--prover", "--private-key", ""].iter()).unwrap();
985 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
986 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
987 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4132").unwrap()));
988 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3032").unwrap()));
989 assert_eq!(trusted_peers.len(), 2);
990 assert_eq!(trusted_validators.len(), 2);
991 assert!(!config.validator);
992 assert!(config.prover);
993 assert!(!config.client);
994 assert_eq!(genesis, expected_genesis);
995
996 let mut trusted_peers = vec![];
997 let mut trusted_validators = vec![];
998 let mut config =
999 Start::try_parse_from(["snarkos", "--dev", "3", "--client", "--private-key", ""].iter()).unwrap();
1000 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1001 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1002 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4133").unwrap()));
1003 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3033").unwrap()));
1004 assert_eq!(trusted_peers.len(), 3);
1005 assert_eq!(trusted_validators.len(), 2);
1006 assert!(!config.validator);
1007 assert!(!config.prover);
1008 assert!(config.client);
1009 assert_eq!(genesis, expected_genesis);
1010 }
1011
1012 #[test]
1013 fn clap_snarkos_start() {
1014 let arg_vec = vec![
1015 "snarkos",
1016 "start",
1017 "--nodisplay",
1018 "--dev",
1019 "2",
1020 "--validator",
1021 "--private-key",
1022 "PRIVATE_KEY",
1023 "--cdn",
1024 "CDN",
1025 "--peers",
1026 "IP1,IP2,IP3",
1027 "--validators",
1028 "IP1,IP2,IP3",
1029 "--rest",
1030 "127.0.0.1:3030",
1031 ];
1032 let cli = CLI::parse_from(arg_vec);
1033
1034 if let Command::Start(start) = cli.command {
1035 assert!(start.nodisplay);
1036 assert_eq!(start.dev, Some(2));
1037 assert!(start.validator);
1038 assert_eq!(start.private_key.as_deref(), Some("PRIVATE_KEY"));
1039 assert_eq!(start.cdn, Some("CDN".to_string()));
1040 assert_eq!(start.rest, Some("127.0.0.1:3030".parse().unwrap()));
1041 assert_eq!(start.network, 0);
1042 assert_eq!(start.peers, "IP1,IP2,IP3");
1043 assert_eq!(start.validators, "IP1,IP2,IP3");
1044 } else {
1045 panic!("Unexpected result of clap parsing!");
1046 }
1047 }
1048}