Skip to main content

snarkos_cli/commands/
start.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkOS library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::helpers::{
17    args::{network_id_parser, parse_node_data_dir},
18    dev::*,
19};
20
21use snarkos_account::Account;
22use snarkos_display::Display;
23use snarkos_node::{
24    Node,
25    bft::MEMORY_POOL_PORT,
26    network::{NodeType, bootstrap_peers},
27    rest::DEFAULT_REST_PORT,
28    router::DEFAULT_NODE_PORT,
29};
30use snarkos_utilities::{NodeDataDir, SignalHandler, jwt_secret_file, node_data};
31
32use snarkvm::{
33    console::{
34        account::{Address, PrivateKey},
35        algorithms::Hash,
36        network::{CanaryV0, MainnetV0, Network, TestnetV0},
37    },
38    ledger::{
39        block::Block,
40        committee::{Committee, MIN_DELEGATOR_STAKE, MIN_VALIDATOR_STAKE},
41        store::{ConsensusStore, helpers::memory::ConsensusMemory},
42    },
43    prelude::{FromBytes, Itertools, ToBits, ToBytes},
44    synthesizer::VM,
45    utilities::to_bytes_le,
46};
47
48use aleo_std::{StorageMode, aleo_ledger_dir};
49use anyhow::{Context, Result, anyhow, bail, ensure};
50use base64::prelude::{BASE64_STANDARD, Engine};
51use clap::Parser;
52use colored::Colorize;
53use core::str::FromStr;
54use indexmap::IndexMap;
55use rand::{RngExt, SeedableRng};
56use rand_chacha::ChaChaRng;
57use serde::{Deserialize, Serialize};
58
59use std::{
60    fs,
61    io::IsTerminal,
62    net::{Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs},
63    path::{Path, PathBuf},
64    sync::{Arc, atomic::AtomicBool},
65};
66use tokio::{
67    runtime::{self, Handle, Runtime},
68    sync::mpsc,
69    task,
70};
71use tracing::{debug, info, warn};
72use ureq::http;
73
74/// The recommended minimum number of 'open files' limit for a validator.
75/// Validators should be able to handle at least 1000 concurrent connections, each requiring 2 sockets.
76#[cfg(target_family = "unix")]
77const RECOMMENDED_MIN_NOFILES_LIMIT: u64 = 2048;
78
79// A mapping of `staker_address` to `(validator_address, withdrawal_address, amount)`.
80#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
81pub struct BondedBalances(IndexMap<String, (String, String, u64)>);
82
83impl FromStr for BondedBalances {
84    type Err = serde_json::Error;
85
86    fn from_str(s: &str) -> Result<Self, Self::Err> {
87        serde_json::from_str(s)
88    }
89}
90
91// Starts the snarkOS node.
92#[derive(Clone, Debug, Parser)]
93#[command(
94    // Use kebab-case for all arguments (e.g., use the `private-key` flag for the `private_key` field).
95    // This is already the default, but we specify it in case clap's default changes in the future.
96    rename_all = "kebab-case",
97
98    // Ensure at most one node type is specified.
99    group(clap::ArgGroup::new("node_type").required(false).multiple(false)
100),
101
102    // Ensure all other dev flags can only be set if `--dev` is set.
103    group(clap::ArgGroup::new("dev_flags").required(false).multiple(true).requires("dev")
104),
105    // Ensure any rest flag (including `--rest`) cannot be set
106    // if `--norest` is set.
107    group(clap::ArgGroup::new("rest_flags").required(false).multiple(true).conflicts_with("norest")),
108
109    // Ensure you cannot set --verbosity and --log-filter flags at the same time.
110    group(clap::ArgGroup::new("log_flags").required(false).multiple(false)),
111
112    // Ensure you need to set either --jwt-secret and --jwt-timestamp or --nojwt flags.
113    group(clap::ArgGroup::new("jwt_flags").required(false).multiple(true).conflicts_with("nojwt").conflicts_with("norest")),
114)]
115pub struct Start {
116    /// Specify the network ID of this node
117    /// [options: 0 = mainnet, 1 = testnet, 2 = canary]
118    #[clap(long, default_value_t=MainnetV0::ID, long, value_parser = network_id_parser())]
119    pub network: u16,
120
121    /// Start the node as a prover.
122    #[clap(long, group = "node_type")]
123    pub prover: bool,
124
125    /// Start the node as a client (default).
126    ///
127    /// Client are "full nodes", i.e, validate and execute all blocks they receive, but they do not participate in AleoBFT consensus.
128    #[clap(long, group = "node_type", verbatim_doc_comment)]
129    pub client: bool,
130
131    /// Start the node as a bootstrap client.
132    #[clap(long = "bootstrap-client", group = "node_type", conflicts_with_all = ["peers", "validators"], verbatim_doc_comment)]
133    pub bootstrap_client: bool,
134
135    /// Start the node as a validator.
136    ///
137    /// Validators are "full nodes", like clients, but also participate in AleoBFT.
138    #[clap(long, group = "node_type", verbatim_doc_comment)]
139    pub validator: bool,
140
141    /// Specify the account private key of the node
142    #[clap(long)]
143    pub private_key: Option<String>,
144
145    /// Specify the path to a file containing the account private key of the node
146    #[clap(long = "private-key-file")]
147    pub private_key_file: Option<PathBuf>,
148
149    /// Set the IP address and port used for P2P communication.
150    #[clap(long)]
151    pub node: Option<SocketAddr>,
152
153    /// Set the IP address and port used for BFT communication.
154    /// This argument is only allowed for validator nodes.
155    #[clap(long, requires = "validator")]
156    pub bft: Option<SocketAddr>,
157
158    /// Specify the host:port address pairs of the peer(s) to connect to (as a comma-separated list).
159    ///
160    /// These peers will be set as "trusted", which means the node will not disconnect from them when performing peer rotation.
161    ///
162    /// Setting peers to "" has the same effect as not setting the flag at all, except when using `--dev`.
163    #[clap(long, verbatim_doc_comment)]
164    pub peers: Option<String>,
165
166    /// Specify the host:port address pairs of the validator(s) to connect to.
167    #[clap(long)]
168    pub validators: Option<String>,
169
170    /// [DEPRECATED] [NO-OP] Allow untrusted peers (not listed in `--peers`) to connect.
171    ///
172    /// The flag will be ignored by client and prover nodes, as this behavior is always enabled for these types of nodes.
173    #[clap(long, verbatim_doc_comment)]
174    pub allow_external_peers: bool,
175
176    /// [DEPRECATED] [NO-OP] If the flag is set, a client will periodically evict more external peers
177    #[clap(long)]
178    pub rotate_external_peers: bool,
179
180    /// Specify the IP address and port for the REST server
181    #[clap(long, group = "rest_flags")]
182    pub rest: Option<SocketAddr>,
183
184    /// Specify the requests per second (RPS) rate limit per IP for the REST server
185    #[clap(long, default_value_t = 10, group = "rest_flags")]
186    pub rest_rps: u32,
187
188    /// Specify the JWT secret for the REST server (16B, base64-encoded).
189    #[clap(long, group = "jwt_flags")]
190    pub jwt_secret: Option<String>,
191
192    /// Specify the JWT creation timestamp; can be any time in the last 10 years.
193    #[clap(long, group = "jwt_flags")]
194    pub jwt_timestamp: Option<i64>,
195
196    /// If the flag is set, the node will not initialize the REST server.
197    #[clap(long)]
198    pub norest: bool,
199
200    /// If the flag is set, the node will not require JWT authentication for the REST server.
201    #[clap(long, group = "rest_flags")]
202    pub nojwt: bool,
203
204    /// If the flag is set, the node will only connect to trusted peers and validators.
205    #[clap(long)]
206    pub trusted_peers_only: bool,
207
208    /// Write log message to stdout instead of showing a terminal UI.
209    ///
210    /// This is useful, for example, for running a node as a service instead of in the foreground or to pipe its output into a file.
211    #[clap(long, verbatim_doc_comment)]
212    pub nodisplay: bool,
213
214    /// Do not show the Aleo banner and information about the node on startup.
215    #[clap(long, hide = true)]
216    pub nobanner: bool,
217
218    /// Specify the log verbosity of the node.
219    /// [options: 0 (lowest log level) to 6 (highest level)]
220    #[clap(long, default_value_t = 1, group = "log_flags")]
221    pub verbosity: u8,
222
223    /// Set a custom log filtering scheme, e.g., "off,snarkos_bft=trace", to show all log messages of snarkos_bft but nothing else.
224    #[clap(long, group = "log_flags")]
225    pub log_filter: Option<String>,
226
227    /// Specify the path to the file where logs will be stored
228    #[clap(long, default_value_os_t = std::env::temp_dir().join("snarkos.log"))]
229    pub logfile: PathBuf,
230
231    /// Enable the metrics exporter
232    #[cfg(feature = "metrics")]
233    #[clap(long)]
234    pub metrics: bool,
235
236    /// Specify the IP address and port for the metrics exporter
237    #[cfg(feature = "metrics")]
238    #[clap(long, requires = "metrics")]
239    pub metrics_ip: Option<SocketAddr>,
240
241    /// Specify the directory that holds all ledger data, e.g., blocks and transactions.
242    /// This flag overrides the default path, even when `--dev` is set.
243    ///
244    /// The old name for this flag (`--storage`) is DEPRECATED and will eventually be removed.
245    #[clap(long, verbatim_doc_comment, alias = "storage")]
246    pub ledger_storage: Option<PathBuf>,
247
248    /// Specify the directory that holds node-specific data, that is not part of the global ledger.
249    /// This flag overrides the default path, even when `--dev` is set.
250    ///
251    /// That folder may contain sensitive data, such as the JWT secret, and should not be shared with untrusted parties.
252    /// For validators, it also contains the latest proposal cache, which is required to participate in consensus.
253    #[clap(long, verbatim_doc_comment)]
254    pub node_data_storage: Option<PathBuf>,
255
256    /// If specified, the node will automatically save database checkpoints.
257    #[clap(long)]
258    pub auto_db_checkpoints: Option<PathBuf>,
259
260    /// Enables the node to prefetch initial blocks from a CDN
261    #[clap(long, conflicts_with = "nocdn")]
262    pub cdn: Option<http::Uri>,
263
264    /// If the flag is set, the node will not prefetch from a CDN
265    #[clap(long)]
266    pub nocdn: bool,
267
268    /// Enables development mode used to set up test networks.
269    ///
270    /// The purpose of this flag is to run multiple nodes on the same machine and in the same working directory.
271    /// To do this, set the value to a unique ID within the test work. For example if there are four nodes in the network, pass `--dev 0` for the first node, `--dev 1` for the second, and so forth.
272    ///
273    /// If you do not explicitly set the `--peers` flag, this will also populate the set of trusted peers, so that the network is fully connected.
274    /// Additionally, if you do not set the `--rest` or the `--norest` flags, it will also set the REST port to `3030` for the first node, `3031` for the second, and so forth.
275    #[clap(long, verbatim_doc_comment)]
276    pub dev: Option<u16>,
277
278    /// If development mode is enabled, specify the number of genesis validator.
279    #[clap(long, group = "dev_flags", default_value_t=DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS)]
280    pub dev_num_validators: u16,
281
282    /// If development mode is enabled, specify the number of clients.
283    /// This is only used by validators to automatically populate their set of trusted peers.
284    ///
285    /// This option cannot be used while also passing the `--peers` flag.
286    #[clap(long, group = "dev_flags", conflicts_with = "peers")]
287    pub dev_num_clients: Option<u16>,
288
289    /// If development mode is enabled, specify whether node 0 should generate traffic to drive the network.
290    #[clap(long, group = "dev_flags")]
291    pub no_dev_txs: bool,
292
293    /// If development mode is enabled, specify the custom bonded balances as a JSON object.
294    #[clap(long, group = "dev_flags")]
295    pub dev_bonded_balances: Option<BondedBalances>,
296
297    /// If development mode is enabled, specify whether to run the node on a production ledger.
298    #[clap(long, group = "dev_flags", requires = "dev_num_validators", default_value_t = false)]
299    pub dev_on_prod: bool,
300
301    /// If the flag is set, the node will attempt to automatically migrate the node data to the new format.
302    #[clap(long)]
303    pub auto_migrate_node_data: bool,
304
305    /// Paths to Slipstream plugin config files (JSON5). May be repeated for multiple plugins.
306    /// Requires the node to be compiled with --features slipstream-plugins.
307    #[cfg(feature = "slipstream-plugins")]
308    #[clap(long = "slipstream-config", value_name = "PATH", verbatim_doc_comment)]
309    pub slipstream_configs: Vec<PathBuf>,
310}
311
312impl Start {
313    /// Starts the snarkOS node and blocks until it terminates.
314    pub fn parse(self) -> Result<String> {
315        // Prepare the shutdown flag.
316        let shutdown: Arc<AtomicBool> = Default::default();
317
318        // Initialize the logger.
319        let log_receiver = crate::helpers::initialize_logger(
320            self.verbosity,
321            &self.log_filter,
322            self.nodisplay,
323            self.logfile.clone(),
324            shutdown.clone(),
325        )
326        .with_context(|| "Failed to set up logger")?;
327
328        // When running in a non-interactive session, disallow the use of the terminal UI.
329        if !std::io::stdout().is_terminal() && !self.nodisplay {
330            anyhow::bail!(
331                "snarkOS cannot use the terminal UI in a non-interactive session. Please restart with `--nodisplay`."
332            );
333        }
334
335        // Initialize the runtime.
336        let runtime = Self::runtime();
337        let handle = runtime.handle().clone();
338        runtime.block_on(async move {
339            // Error messages.
340            let node_parse_error = || "Failed to start node";
341
342            // Clone the configurations.
343            let mut self_ = self.clone();
344
345            // Parse the node arguments, start it, and block until shutdown.
346            match self_.network {
347                MainnetV0::ID => {
348                    self_.parse_node::<MainnetV0>(handle, log_receiver).await.with_context(node_parse_error)?
349                }
350                TestnetV0::ID => {
351                    self_.parse_node::<TestnetV0>(handle, log_receiver).await.with_context(node_parse_error)?
352                }
353                CanaryV0::ID => {
354                    self_.parse_node::<CanaryV0>(handle, log_receiver).await.with_context(node_parse_error)?
355                }
356                _ => panic!("Invalid network ID specified"),
357            };
358
359            Ok(String::new())
360        })
361    }
362}
363
364impl Start {
365    /// Returns the initial peer(s) to connect to, from the given configurations.
366    fn parse_trusted_addrs(&self, list: &Option<String>) -> Result<Vec<SocketAddr>> {
367        let Some(list) = list else { return Ok(vec![]) };
368
369        match list.is_empty() {
370            // Split on an empty string returns an empty string.
371            true => Ok(vec![]),
372            false => list.split(',').map(resolve_potential_hostnames).collect(),
373        }
374    }
375
376    /// Returns the CDN to prefetch initial blocks from, or `None` if fetching from the CDN is disabled.
377    fn parse_cdn<N: Network>(&self) -> Result<Option<http::Uri>> {
378        // Disable CDN if:
379        //  1. The node is in development mode.
380        //  2. The user has explicitly disabled CDN.
381        //  3. The node is a prover (no need to sync).
382        let no_cdn_reasons = [("--dev", self.dev.is_some()), ("--nocdn", self.nocdn), ("--prover", self.prover)]
383            .into_iter()
384            .filter_map(|(reason, flag_set)| flag_set.then_some(reason))
385            .join(" and ");
386        if !no_cdn_reasons.is_empty() {
387            info!("CDN disabled because the following flags are set: {no_cdn_reasons}.");
388            Ok(None)
389        }
390        // Enable the CDN otherwise.
391        else {
392            // Determine the CDN URL.
393            match &self.cdn {
394                // Use the provided CDN URL if it is not empty.
395                Some(cdn) => match cdn.to_string().is_empty() {
396                    true => Ok(None),
397                    false => Ok(Some(cdn.clone())),
398                },
399                // If no CDN URL is provided, determine the CDN URL based on the network ID.
400                None => {
401                    let uri = format!("{}/{}", snarkos_node_cdn::CDN_BASE_URL, N::SHORT_NAME);
402                    Ok(Some(http::Uri::try_from(&uri).with_context(|| "Unexpected error")?))
403                }
404            }
405        }
406    }
407
408    /// Read the private key directly from an argument or from a filesystem location,
409    /// returning the Aleo account.
410    fn parse_private_key<N: Network>(&self) -> Result<Account<N>> {
411        match self.dev {
412            None => match (&self.private_key, &self.private_key_file) {
413                // Parse the private key directly.
414                (Some(private_key), None) => Account::from_str(private_key.trim()),
415                // Parse the private key from a file.
416                (None, Some(path)) => {
417                    check_permissions(path)?;
418                    Account::from_str(std::fs::read_to_string(path)?.trim())
419                }
420                // Ensure the private key is provided to the CLI, except for clients or nodes in development mode.
421                (None, None) => match self.client {
422                    true => Account::new(&mut rand::rng()),
423                    false => bail!("Missing the '--private-key' or '--private-key-file' argument"),
424                },
425                // Ensure only one private key flag is provided to the CLI.
426                (Some(_), Some(_)) => {
427                    bail!("Cannot use '--private-key' and '--private-key-file' simultaneously, please use only one")
428                }
429            },
430            Some(index) => {
431                let private_key = get_development_key(index)?;
432                if !self.nobanner {
433                    println!(
434                        "🔑 Your development private key for node {index} is {}.\n",
435                        private_key.to_string().bold()
436                    );
437                }
438                Account::try_from(private_key)
439            }
440        }
441    }
442
443    /// Updates the configurations if the node is in development mode.
444    fn parse_development(
445        &mut self,
446        trusted_peers: &mut Vec<SocketAddr>,
447        trusted_validators: &mut Vec<SocketAddr>,
448    ) -> Result<()> {
449        // If `--dev` is not set, return early.
450        let Some(dev) = self.dev else {
451            return Ok(());
452        };
453
454        // Determine the number of development validators.
455        let num_validators = self.dev_num_validators;
456        ensure!(num_validators >= 4, "Value for `dev_num_validators` is too low. Needs to be at least 4.");
457
458        // If `--dev` is set, assume the dev nodes are initialized from 0 to `dev`,
459        // and add each of them to the trusted peers. In addition, set the node IP to `4130 + dev`,
460        // and the REST port to `3030 + dev`.
461        info!("Development mode enabled with index={dev} and num_validators={num_validators}.");
462
463        // Nodes only start as validators if the `--validator` flag is set, because the default mode is "client".
464        let is_validator = self.validator;
465
466        // Ensure the node type and `dev_num_validators` are compatible.
467        if is_validator {
468            ensure!(
469                dev < num_validators,
470                "Development validator index is too high (dev={dev}, dev_num_validators={num_validators})",
471            );
472        }
473        // A dev client or prover is allowed to have an index lower than
474        // `dev_num_validators` in order to have a balance at startup.
475
476        // Add the dev nodes to the trusted validators.
477        if trusted_validators.is_empty() && is_validator {
478            // Validators add all other validators as trusted.
479            for idx in 0..num_validators {
480                if idx == dev {
481                    continue;
482                }
483                trusted_validators.push(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, MEMORY_POOL_PORT + idx)));
484            }
485
486            debug!("Trusted validators set to: {trusted_validators:?}");
487        }
488
489        // Determine if we need to populate `trusted_peers`.
490        if trusted_peers.is_empty() {
491            if is_validator {
492                if let Some(num_clients) = self.dev_num_clients {
493                    // Ensure the clients that added this validator as a trusted peer are able to connect to it.
494                    for client_idx in 0..num_clients {
495                        if get_devnet_validators_for_client(client_idx, num_validators).contains(&dev) {
496                            let node_idx = num_validators + client_idx;
497                            trusted_peers.push(get_devnet_router_address_for_node(node_idx));
498                        }
499                    }
500                } else {
501                    warn!(
502                        "Development validator started without trusted peers or `--dev-num-clients`. No clients will be able to connect to it."
503                    );
504                }
505            } else {
506                // Clients/provers add two validators to connect to.
507                for validator_idx in get_devnet_validators_for_client(dev, num_validators) {
508                    trusted_peers.push(get_devnet_router_address_for_node(validator_idx));
509                }
510            }
511
512            debug!("Trusted peers set to: {trusted_peers:?}");
513        } else {
514            debug!("Trusted peers/validators was set manually. Will not populate them with development addresses.")
515        }
516
517        // Set the node's listening port to `4130 + dev`.
518        //
519        // Note: the `node` flag is an option to detect remote devnet testing.
520        if self.node.is_none() {
521            // Pick 0.0.0.0 here, not localhost.
522            let port = get_devnet_router_address_for_node(dev).port();
523            let address = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port));
524            debug!("Setting node address to {address} due to dev={dev}");
525            self.node = Some(address);
526        }
527
528        // If the `norest` flag is not set and the REST IP is not already specified set the REST IP to `3030 + dev`.
529        if !self.norest && self.rest.is_none() {
530            let port = DEFAULT_REST_PORT + dev;
531            debug!("Setting REST port to {port} due to dev={dev}");
532            self.rest = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port)));
533        }
534
535        Ok(())
536    }
537
538    /// Returns the path to where the JWT secret for the node is stored.
539    fn jwt_secret_path<N: Network>(node_data_dir: &NodeDataDir, address: &Address<N>) -> PathBuf {
540        node_data_dir.path().join(jwt_secret_file(address))
541    }
542
543    /// Returns an alternative genesis block if the node is in development mode.
544    /// Otherwise, returns the actual genesis block.
545    fn parse_genesis<N: Network>(&self) -> Result<Block<N>> {
546        if self.dev.is_some() && !self.dev_on_prod {
547            // Determine the number of genesis committee members.
548            let num_committee_members = self.dev_num_validators;
549            ensure!(
550                num_committee_members >= DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
551                "Number of genesis committee members is too low"
552            );
553
554            // Initialize the (fixed) RNG.
555            let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
556            // Initialize the development private keys.
557            let dev_keys =
558                (0..num_committee_members).map(|_| PrivateKey::<N>::new(&mut rng)).collect::<Result<Vec<_>>>()?;
559            // Initialize the development addresses.
560            let development_addresses = dev_keys.iter().map(Address::<N>::try_from).collect::<Result<Vec<_>>>()?;
561
562            // Construct the committee based on the state of the bonded balances.
563            let (committee, bonded_balances) = match &self.dev_bonded_balances {
564                Some(bonded_balances) => {
565                    // Parse the bonded balances.
566                    let bonded_balances = bonded_balances
567                        .0
568                        .iter()
569                        .map(|(staker_address, (validator_address, withdrawal_address, amount))| {
570                            let staker_addr = Address::<N>::from_str(staker_address)?;
571                            let validator_addr = Address::<N>::from_str(validator_address)?;
572                            let withdrawal_addr = Address::<N>::from_str(withdrawal_address)?;
573                            Ok((staker_addr, (validator_addr, withdrawal_addr, *amount)))
574                        })
575                        .collect::<Result<IndexMap<_, _>>>()?;
576
577                    // Construct the committee members.
578                    let mut members = IndexMap::new();
579                    for (staker_address, (validator_address, _, amount)) in bonded_balances.iter() {
580                        // Ensure that the staking amount is sufficient.
581                        match staker_address == validator_address {
582                            true => ensure!(amount >= &MIN_VALIDATOR_STAKE, "Validator stake is too low"),
583                            false => ensure!(amount >= &MIN_DELEGATOR_STAKE, "Delegator stake is too low"),
584                        }
585
586                        // Ensure that the validator address is included in the list of development addresses.
587                        ensure!(
588                            development_addresses.contains(validator_address),
589                            "Validator address {validator_address} is not included in the list of development addresses"
590                        );
591
592                        // Add or update the validator entry in the list of members
593                        members.entry(*validator_address).and_modify(|(stake, _, _)| *stake += amount).or_insert((
594                            *amount,
595                            true,
596                            rng.random_range(0..100),
597                        ));
598                    }
599                    // Construct the committee.
600                    let committee = Committee::<N>::new(0u64, members)?;
601                    (committee, bonded_balances)
602                }
603                None => {
604                    // Calculate the committee stake per member.
605                    let stake_per_member =
606                        N::STARTING_SUPPLY.saturating_div(2).saturating_div(num_committee_members as u64);
607                    ensure!(stake_per_member >= MIN_VALIDATOR_STAKE, "Committee stake per member is too low");
608
609                    // Construct the committee members and distribute stakes evenly among committee members.
610                    let members = development_addresses
611                        .iter()
612                        .map(|address| (*address, (stake_per_member, true, rng.random_range(0..100))))
613                        .collect::<IndexMap<_, _>>();
614
615                    // Construct the bonded balances.
616                    // Note: The withdrawal address is set to the staker address.
617                    let bonded_balances = members
618                        .iter()
619                        .map(|(address, (stake, _, _))| (*address, (*address, *address, *stake)))
620                        .collect::<IndexMap<_, _>>();
621                    // Construct the committee.
622                    let committee = Committee::<N>::new(0u64, members)?;
623
624                    (committee, bonded_balances)
625                }
626            };
627
628            // Ensure that the number of committee members is correct.
629            ensure!(
630                committee.members().len() == num_committee_members as usize,
631                "Number of committee members {} does not match the expected number of members {num_committee_members}",
632                committee.members().len()
633            );
634
635            // Calculate the public balance per validator.
636            let remaining_balance = N::STARTING_SUPPLY.saturating_sub(committee.total_stake());
637            let public_balance_per_validator = remaining_balance.saturating_div(num_committee_members as u64);
638
639            // Construct the public balances with fairly equal distribution.
640            let mut public_balances = dev_keys
641                .iter()
642                .map(|private_key| Ok((Address::try_from(private_key)?, public_balance_per_validator)))
643                .collect::<Result<indexmap::IndexMap<_, _>>>()?;
644
645            // If there is some leftover balance, add it to the 0-th validator.
646            let leftover =
647                remaining_balance.saturating_sub(public_balance_per_validator * num_committee_members as u64);
648            if leftover > 0 {
649                let (_, balance) = public_balances.get_index_mut(0).unwrap();
650                *balance += leftover;
651            }
652
653            // Check if the sum of committee stakes and public balances equals the total starting supply.
654            let public_balances_sum: u64 = public_balances.values().copied().sum();
655            if committee.total_stake() + public_balances_sum != N::STARTING_SUPPLY {
656                bail!("Sum of committee stakes and public balances does not equal total starting supply.");
657            }
658
659            // Construct the genesis block.
660            std::thread::spawn(move || {
661                load_or_compute_genesis(dev_keys[0], committee, public_balances, bonded_balances, &mut rng)
662            })
663            .join()
664            .unwrap()
665        } else {
666            Block::from_bytes_le(N::genesis_bytes())
667        }
668    }
669
670    /// Returns the node type specified in the command-line arguments.
671    /// This will return `NodeType::Client` if no node type was specified by the user.
672    const fn parse_node_type(&self) -> NodeType {
673        if self.validator {
674            NodeType::Validator
675        } else if self.prover {
676            NodeType::Prover
677        } else if self.bootstrap_client {
678            NodeType::BootstrapClient
679        } else {
680            NodeType::Client
681        }
682    }
683
684    /// Start the node and blocks until it terminates.
685    #[rustfmt::skip]
686    async fn parse_node<N: Network>(&mut self, handle: Handle, log_receiver: mpsc::Receiver<Vec<u8>>) -> Result<()> {
687        if !self.nobanner {
688            // Print the welcome banner.
689            println!("{}", crate::helpers::welcome_message());
690        }
691
692        // Only allow dev mode if we built with the 'test_network' feature.
693        if self.dev.is_some() && cfg!(not(feature = "test_network")) {
694            bail!("The 'dev' flag is set, but the 'test_network' feature is not enabled");
695        }
696
697        // Parse the trusted peers to connect to.
698        let mut trusted_peers = self.parse_trusted_addrs(&self.peers)?;
699        // Parse the trusted validators to connect to.
700        let mut trusted_validators = self.parse_trusted_addrs(&self.validators)?;
701
702        // Ensure there are no bootstrappers among the trusted peers and validators.
703        let bootstrap_peers = bootstrap_peers::<N>(self.dev.is_some());
704        for trusted in [&mut trusted_peers, &mut trusted_validators] {
705            let initial_peer_count = trusted.len();
706            trusted.retain(|addr| !bootstrap_peers.contains(addr));
707            let final_peer_count = trusted.len();
708            // Warn if this had to be corrected.
709            if final_peer_count != initial_peer_count {
710                warn!(
711                    "Removed some ({}) trusted peers due to them also being bootstrap peers.",
712                    initial_peer_count - final_peer_count
713                );
714            }
715        }
716
717        // Parse the development configurations.
718        self.parse_development(&mut trusted_peers, &mut trusted_validators)?;
719
720        // Determine if the node should sync from CDn..
721        let cdn = self.parse_cdn::<N>().with_context(|| "Failed to parse given CDN URL")?;
722
723        // Parse the genesis block.
724        let start = self.clone();
725        let genesis = task::spawn_blocking(move || start.parse_genesis::<N>()).await??;
726        // Parse the private key of the node.
727        let account = self.parse_private_key::<N>()?;
728        // Parse the node type.
729        let node_type = self.parse_node_type();
730
731        // Parse the node IP or use the default IP/port.
732        let node_ip = self.node.unwrap_or(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_NODE_PORT)));
733
734        // Parse the REST IP.
735        let rest_ip = match self.norest {
736            true => None,
737            false => self.rest.or_else(|| Some("0.0.0.0:3030".parse().unwrap())),
738        };
739
740        // Initialize the storage mode.
741        let storage_mode = match &self.ledger_storage {
742            Some(path) => StorageMode::Custom(path.clone()),
743            None => match self.dev {
744                Some(id) => StorageMode::Development(id),
745                None => StorageMode::Production,
746            },
747        };
748
749        // Users may have unintentionally set a custom path for the ledger, but not for the node data.
750        // For validators, we make this an errors, so important files like the proposal cache are stored at the location
751        // exepcted by the node operator.
752        if self.node_data_storage.is_some() && !matches!(storage_mode, StorageMode::Custom(_)) {
753            if node_type == NodeType::Validator {
754                bail!("Custom path set for `--node-data-storage`, but not for `--ledger-storage`.")
755            } else {
756                warn!("Custom path set for `--node-data-storage`, but not for `--ledger-storage`. The latter will use the default path.");   
757            }
758        } else if matches!(storage_mode, StorageMode::Custom(_)) && self.node_data_storage.is_none() {
759            if node_type == NodeType::Validator {
760                bail!("Custom path set for `--ledger-storage`, but not for `--node-data-storage`.");
761            } else {
762                warn!("Custom path set for `--ledger-storage`, but not for `--node-data-storage`. The latter will use the default path.");
763            }
764        }
765
766        // Parse the node data directory.
767        let node_data_dir = parse_node_data_dir(&self.node_data_storage, N::ID, self.dev).with_context(|| "Failed to setup node configuration directory")?;
768
769        // Make sure the directory exists before continue.
770        let data_path = node_data_dir.path();
771        if !data_path.exists() {
772            info!("Creating directore for node data storage at {data_path:?}");
773            std::fs::create_dir_all(data_path)
774                .with_context(|| format!("Failed to create directory for node data storage at {data_path:?}"))?
775        } else if !data_path.is_dir() {
776            bail!("Node data storage location at {data_path:?} is not a directory");
777        } else {
778            debug!("Using existing directory at {data_path:?} for node data storage");
779        }
780
781        // Checks for the old storage format and prints instructions to migrate.
782        // We perform this check after creating the node data directory, so that migrating the data is easier.
783        Self::check_for_old_storage_format(&aleo_ledger_dir(N::ID, &storage_mode), &account.address(), &node_data_dir, self.dev, self.auto_migrate_node_data).with_context(|| "Node still uses the old storage format.")?;
784
785        // Compute the optional REST server JWT.
786        let jwt_token = if self.nojwt {
787            None
788        } else if let Some(jwt_b64) = &self.jwt_secret {
789            // Decode the JWT secret.
790            let jwt_bytes = BASE64_STANDARD.decode(jwt_b64).map_err(|_| anyhow::anyhow!("Invalid JWT secret"))?;
791            if jwt_bytes.len() != 16 {
792                bail!("The JWT secret must be 16 bytes long");
793            }
794            // Create the JWT token based on the given secret.
795            let jwt_token = snarkos_node_rest::Claims::new(account.address(), Some(jwt_bytes), self.jwt_timestamp).to_jwt_string()?;
796            // Store the JWT secret to a file.
797            let path = Self::jwt_secret_path(&node_data_dir, &account.address());
798            std::fs::write(path, &jwt_token)?;
799            // Return the JWT token for optional printing.
800            Some(jwt_token)
801        } else {
802            // Create a random JWT token.
803            let jwt_token = snarkos_node_rest::Claims::new(account.address(), None, self.jwt_timestamp).to_jwt_string()?;
804            // Store the JWT secret to a file.
805            let path = Self::jwt_secret_path(&node_data_dir, &account.address());
806            std::fs::write(path, &jwt_token)? ;
807            // Return the JWT token for optional printing.
808            Some(jwt_token)
809        };
810
811        if !self.nobanner {
812            // Print the Aleo address.
813            println!("👛 Your Aleo address is {}.\n", account.address().to_string().bold());
814            // Print the node type and network.
815            println!(
816                "🧭 Starting {} on {} at {}.\n",
817                node_type.description().bold(),
818                N::NAME.bold(),
819                node_ip.to_string().bold()
820            );
821            // If the node is running a REST server, determine the JWT.
822            if let Some(rest_ip) = rest_ip {
823                println!("🌐 Starting the REST server at {}.\n", rest_ip.to_string().bold());
824                if let Some(jwt_token) = jwt_token {
825                    println!("🔑 Your one-time JWT token is {}\n", jwt_token.dimmed());
826                }
827            }
828        }
829
830        // If the node is a validator, check if the open files limit is lower than recommended.
831        #[cfg(target_family = "unix")]
832        if node_type.is_validator() {
833            crate::helpers::check_open_files_limit(RECOMMENDED_MIN_NOFILES_LIMIT);
834        }
835        // Check if the machine meets the minimum requirements for a validator.
836        crate::helpers::check_validator_machine(node_type);
837
838        // Initialize the metrics.
839        #[cfg(feature = "metrics")]
840        if self.metrics {
841            metrics::initialize_metrics(self.metrics_ip);
842        }
843
844        // Determine whether to generate background transactions in dev mode.
845        let dev_txs = match self.dev {
846            Some(_) => !self.no_dev_txs,
847            None => {
848                // If the `no_dev_txs` flag is set, inform the user that it is ignored.
849                if self.no_dev_txs {
850                    eprintln!("The '--no-dev-txs' flag is ignored because '--dev' is not set");
851                }
852                false
853            }
854        };
855
856        // Determine the number of validators for the committee hotswap.
857        let dev_num_validators_for_committee_hotswap = self.dev_on_prod.then_some(self.dev_num_validators);
858
859        // TODO(kaimast): start the display earlier and show sync progress.
860        if !self.nodisplay && cdn.is_some() {
861            println!("🪧 The terminal UI will not start until the node has finished syncing from the CDN. If this step takes too long, consider restarting with `--nodisplay`.");
862        }
863
864        // Register the signal handler.
865        let signal_handler = SignalHandler::new(Some(handle));
866
867        // Collect slipstream plugin config paths (empty slice when feature is disabled).
868        #[cfg(feature = "slipstream-plugins")]
869        let slipstream_configs: &[PathBuf] = &self.slipstream_configs;
870        #[cfg(not(feature = "slipstream-plugins"))]
871        let slipstream_configs: &[PathBuf] = &[];
872
873        // Initialize the node.
874        let node = match node_type {
875            // NodeType::Validator => Node::new_validator(node_ip, self.bft, rest_ip, self.rest_rps, account, &trusted_peers, &trusted_validators, genesis, cdn, storage_mode, node_data_dir, self.trusted_peers_only, self.auto_db_checkpoints.clone(), dev_txs, self.dev, slipstream_configs, signal_handler.clone()).await,
876            NodeType::Validator => Node::new_validator(node_ip, self.bft, rest_ip, self.rest_rps, account, &trusted_peers, &trusted_validators, genesis, cdn, storage_mode, node_data_dir, self.trusted_peers_only, self.auto_db_checkpoints.clone(), dev_txs, self.dev, slipstream_configs, dev_num_validators_for_committee_hotswap, signal_handler.clone()).await,
877            NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, node_data_dir, self.trusted_peers_only, self.dev, signal_handler.clone()).await,
878            NodeType::Client => Node::new_client(node_ip, rest_ip, self.rest_rps, account, &trusted_peers, genesis, cdn, storage_mode, node_data_dir, self.trusted_peers_only, self.auto_db_checkpoints.clone(), self.dev, slipstream_configs, signal_handler.clone()).await,
879            NodeType::BootstrapClient => Node::new_bootstrap_client(node_ip, account, *genesis.header(), self.dev).await,
880        }?;
881
882        if !self.nodisplay {
883            Display::start(node.clone(), log_receiver, signal_handler.clone()).with_context(|| "Failed to start the display")?;
884        }
885
886        node.wait_for_signals(&signal_handler).await;
887        Ok(())
888    }
889
890    /// Check if the node is still using the old storage format,
891    /// in which case we print an error and exit.
892    /// We detect this by checking if
893    /// - a peer-cache file exists inside the ledger directory,
894    /// - a current-proposal-cache file exists at the parent directory of the ledger directory
895    /// - a jwt_secret_*.txt file exists at the parent directory of the ledger directory
896    fn check_for_old_storage_format<N: Network>(
897        ledger_dir: &Path,
898        address: &Address<N>,
899        node_data_dir: &NodeDataDir,
900        dev: Option<u16>,
901        auto_migrate: bool,
902    ) -> Result<()> {
903        let ledger_parent_dir = ledger_dir.parent().unwrap();
904
905        // Determine the old paths used for node configuration files.
906        let old_router_cache_path = ledger_dir.join(node_data::LEGACY_ROUTER_PEER_CACHE_FILE);
907        let old_gateway_cache_path = ledger_dir.join(node_data::LEGACY_GATEWAY_PEER_CACHE_FILE);
908        let old_proposal_cache_path = ledger_dir.join(node_data::legacy_current_proposal_cache_file(N::ID, dev));
909        let old_jwt_secret_path = ledger_parent_dir.join(node_data::jwt_secret_file(address));
910
911        if auto_migrate {
912            if old_router_cache_path.exists() {
913                let new_router_cache_path = node_data_dir.router_peer_cache_path();
914                info!("Migrating node data file \"{old_router_cache_path:?}\" to \"{new_router_cache_path:?}\"");
915                fs::rename(old_router_cache_path, new_router_cache_path)
916                    .with_context(|| "Failed to migrate node data file")?;
917            }
918
919            if old_gateway_cache_path.exists() {
920                let new_gateway_cache_path = node_data_dir.gateway_peer_cache_path();
921                info!("Migrating node data file \"{old_gateway_cache_path:?}\" to \"{new_gateway_cache_path:?}\"");
922                fs::rename(old_gateway_cache_path, new_gateway_cache_path)
923                    .with_context(|| "Failed to migrate node data file")?;
924            }
925
926            if old_proposal_cache_path.exists() {
927                let new_proposal_cache_path = node_data_dir.current_proposal_cache_path();
928                info!("Migrating node data file \"{old_proposal_cache_path:?}\" to \"{new_proposal_cache_path:?}\"");
929                fs::rename(old_proposal_cache_path, new_proposal_cache_path)
930                    .with_context(|| "Failed to migrate node data file")?;
931            }
932
933            if old_jwt_secret_path.exists() {
934                let new_jwt_secret_path = node_data_dir.jwt_secret_path(&address);
935                info!("Migrating node data file \"{old_jwt_secret_path:?}\" to \"{new_jwt_secret_path:?}\"");
936                fs::rename(old_jwt_secret_path, new_jwt_secret_path)
937                    .with_context(|| "Failed to migrate node data file")?;
938            }
939        } else {
940            if old_router_cache_path.exists() {
941                let new_router_cache_path = node_data_dir.router_peer_cache_path();
942                bail!(
943                    "Please migrate the node data file \"{old_router_cache_path:?}\" to \"{new_router_cache_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
944                );
945            }
946
947            if old_gateway_cache_path.exists() {
948                let new_gateway_cache_path = node_data_dir.gateway_peer_cache_path();
949                bail!(
950                    "Please migrate the node data file \"{old_gateway_cache_path:?}\" to \"{new_gateway_cache_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
951                );
952            }
953
954            if old_proposal_cache_path.exists() {
955                let new_proposal_cache_path = node_data_dir.current_proposal_cache_path();
956                bail!(
957                    "Please migrate the node data file \"{old_proposal_cache_path:?}\" to \"{new_proposal_cache_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
958                );
959            }
960
961            if old_jwt_secret_path.exists() {
962                let new_jwt_secret_path = node_data_dir.jwt_secret_path(&address);
963                bail!(
964                    "Please migrate the node data file \"{old_jwt_secret_path:?}\" to \"{new_jwt_secret_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
965                );
966            }
967        }
968
969        Ok(())
970    }
971
972    /// Starts a rayon thread pool and tokio runtime for the node, and returns the tokio `Runtime`.
973    fn runtime() -> Runtime {
974        // Retrieve the number of cores.
975        let num_cores = num_cpus::get();
976
977        // Initialize the number of tokio worker threads, max tokio blocking threads, and rayon cores.
978        // Note: We intentionally set the number of tokio worker threads and number of rayon cores to be
979        // more than the number of physical cores, because the node is expected to be I/O-bound.
980        let (num_tokio_worker_threads, max_tokio_blocking_threads, num_rayon_cores_global) =
981            (2 * num_cores, 512, num_cores);
982
983        // Set up the rayon thread pool.
984        // A custom panic handler is not needed here, as rayon propagates the panic to the calling thread by default (except for `rayon::spawn` which we do not use).
985        rayon::ThreadPoolBuilder::new()
986            .stack_size(8 * 1024 * 1024)
987            .num_threads(num_rayon_cores_global)
988            .build_global()
989            .unwrap();
990
991        // Set up the tokio Runtime.
992        // TODO(kaimast): set up a panic handler here for each worker thread once [`tokio::runtime::Builder::unhandled_panic`](https://docs.rs/tokio/latest/tokio/runtime/struct.Builder.html#method.unhandled_panic) is stabilized.
993        runtime::Builder::new_multi_thread()
994            .enable_all()
995            .thread_stack_size(8 * 1024 * 1024)
996            .worker_threads(num_tokio_worker_threads)
997            .max_blocking_threads(max_tokio_blocking_threads)
998            .build()
999            .expect("Failed to initialize a runtime for the router")
1000    }
1001}
1002
1003/// Checks whether a file can only be read/written by the owner. It also allows more restrictive permissions, where only the owner can read it.
1004fn check_permissions(path: &PathBuf) -> Result<(), snarkvm::prelude::Error> {
1005    #[cfg(target_family = "unix")]
1006    {
1007        use std::os::unix::fs::PermissionsExt;
1008        ensure!(path.exists(), "The file '{path:?}' does not exist");
1009        crate::check_parent_permissions(path)?;
1010
1011        let permissions = path.metadata()?.permissions().mode();
1012        ensure!(
1013            matches!(permissions & 0o777, 0o400 | 0o600),
1014            "The file {} must be readable and writable only by the owner (0600)",
1015            path.display()
1016        );
1017    }
1018    Ok(())
1019}
1020
1021/// Loads or computes the genesis block.
1022fn load_or_compute_genesis<N: Network>(
1023    genesis_private_key: PrivateKey<N>,
1024    committee: Committee<N>,
1025    public_balances: indexmap::IndexMap<Address<N>, u64>,
1026    bonded_balances: indexmap::IndexMap<Address<N>, (Address<N>, Address<N>, u64)>,
1027    rng: &mut ChaChaRng,
1028) -> Result<Block<N>> {
1029    // Construct the preimage.
1030    let mut preimage = Vec::new();
1031
1032    // Input the network ID.
1033    preimage.extend(&N::ID.to_le_bytes());
1034    // Input the genesis coinbase target.
1035    preimage.extend(&to_bytes_le![N::GENESIS_COINBASE_TARGET]?);
1036    // Input the genesis proof target.
1037    preimage.extend(&to_bytes_le![N::GENESIS_PROOF_TARGET]?);
1038
1039    // Input the genesis private key, committee, and public balances.
1040    preimage.extend(genesis_private_key.to_bytes_le()?);
1041    preimage.extend(committee.to_bytes_le()?);
1042    preimage.extend(&to_bytes_le![public_balances.iter().collect::<Vec<(_, _)>>()]?);
1043    preimage.extend(&to_bytes_le![
1044        bonded_balances
1045            .iter()
1046            .flat_map(|(staker, (validator, withdrawal, amount))| to_bytes_le![staker, validator, withdrawal, amount])
1047            .collect::<Vec<_>>()
1048    ]?);
1049
1050    // Input the parameters' metadata based on network
1051    match N::ID {
1052        snarkvm::console::network::MainnetV0::ID => {
1053            preimage.extend(snarkvm::parameters::mainnet::BondValidatorVerifier::METADATA.as_bytes());
1054            preimage.extend(snarkvm::parameters::mainnet::BondPublicVerifier::METADATA.as_bytes());
1055            preimage.extend(snarkvm::parameters::mainnet::UnbondPublicVerifier::METADATA.as_bytes());
1056            preimage.extend(snarkvm::parameters::mainnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
1057            preimage.extend(snarkvm::parameters::mainnet::SetValidatorStateVerifier::METADATA.as_bytes());
1058            preimage.extend(snarkvm::parameters::mainnet::TransferPrivateVerifier::METADATA.as_bytes());
1059            preimage.extend(snarkvm::parameters::mainnet::TransferPublicVerifier::METADATA.as_bytes());
1060            preimage.extend(snarkvm::parameters::mainnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
1061            preimage.extend(snarkvm::parameters::mainnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
1062            preimage.extend(snarkvm::parameters::mainnet::FeePrivateVerifier::METADATA.as_bytes());
1063            preimage.extend(snarkvm::parameters::mainnet::FeePublicVerifier::METADATA.as_bytes());
1064            preimage.extend(snarkvm::parameters::mainnet::InclusionVerifier::METADATA.as_bytes());
1065        }
1066        snarkvm::console::network::TestnetV0::ID => {
1067            preimage.extend(snarkvm::parameters::testnet::BondValidatorVerifier::METADATA.as_bytes());
1068            preimage.extend(snarkvm::parameters::testnet::BondPublicVerifier::METADATA.as_bytes());
1069            preimage.extend(snarkvm::parameters::testnet::UnbondPublicVerifier::METADATA.as_bytes());
1070            preimage.extend(snarkvm::parameters::testnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
1071            preimage.extend(snarkvm::parameters::testnet::SetValidatorStateVerifier::METADATA.as_bytes());
1072            preimage.extend(snarkvm::parameters::testnet::TransferPrivateVerifier::METADATA.as_bytes());
1073            preimage.extend(snarkvm::parameters::testnet::TransferPublicVerifier::METADATA.as_bytes());
1074            preimage.extend(snarkvm::parameters::testnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
1075            preimage.extend(snarkvm::parameters::testnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
1076            preimage.extend(snarkvm::parameters::testnet::FeePrivateVerifier::METADATA.as_bytes());
1077            preimage.extend(snarkvm::parameters::testnet::FeePublicVerifier::METADATA.as_bytes());
1078            preimage.extend(snarkvm::parameters::testnet::InclusionVerifier::METADATA.as_bytes());
1079        }
1080        snarkvm::console::network::CanaryV0::ID => {
1081            preimage.extend(snarkvm::parameters::canary::BondValidatorVerifier::METADATA.as_bytes());
1082            preimage.extend(snarkvm::parameters::canary::BondPublicVerifier::METADATA.as_bytes());
1083            preimage.extend(snarkvm::parameters::canary::UnbondPublicVerifier::METADATA.as_bytes());
1084            preimage.extend(snarkvm::parameters::canary::ClaimUnbondPublicVerifier::METADATA.as_bytes());
1085            preimage.extend(snarkvm::parameters::canary::SetValidatorStateVerifier::METADATA.as_bytes());
1086            preimage.extend(snarkvm::parameters::canary::TransferPrivateVerifier::METADATA.as_bytes());
1087            preimage.extend(snarkvm::parameters::canary::TransferPublicVerifier::METADATA.as_bytes());
1088            preimage.extend(snarkvm::parameters::canary::TransferPrivateToPublicVerifier::METADATA.as_bytes());
1089            preimage.extend(snarkvm::parameters::canary::TransferPublicToPrivateVerifier::METADATA.as_bytes());
1090            preimage.extend(snarkvm::parameters::canary::FeePrivateVerifier::METADATA.as_bytes());
1091            preimage.extend(snarkvm::parameters::canary::FeePublicVerifier::METADATA.as_bytes());
1092            preimage.extend(snarkvm::parameters::canary::InclusionVerifier::METADATA.as_bytes());
1093        }
1094        _ => {
1095            // Unrecognized Network ID
1096            bail!("Unrecognized Network ID: {}", N::ID);
1097        }
1098    }
1099
1100    // Initialize the hasher.
1101    let hasher = snarkvm::console::algorithms::BHP256::<N>::setup("aleo.dev.block")?;
1102    // Compute the hash.
1103    // NOTE: this is a fast-to-compute but *IMPERFECT* identifier for the genesis block;
1104    //       to know the actual genesis block hash, you need to compute the block itself.
1105    let hash = hasher.hash(&preimage.to_bits_le())?.to_string();
1106
1107    // A closure to load the block.
1108    let load_block = |file_path| -> Result<Block<N>> {
1109        // Attempts to load the genesis block file locally.
1110        let buffer = std::fs::read(file_path)?;
1111        // Return the genesis block.
1112        Block::from_bytes_le(&buffer)
1113    };
1114
1115    // Construct the file path.
1116    let file_path = std::env::temp_dir().join(hash);
1117    // Check if the genesis block exists.
1118    if file_path.exists() {
1119        // If the block loads successfully, return it.
1120        if let Ok(block) = load_block(&file_path) {
1121            return Ok(block);
1122        }
1123    }
1124
1125    /* Otherwise, compute the genesis block and store it. */
1126
1127    // Initialize a new VM.
1128    let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::new_test(None))?)?;
1129    // Initialize the genesis block.
1130    let block = vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng)?;
1131    // Write the genesis block to the file.
1132    std::fs::write(&file_path, block.to_bytes_le()?)?;
1133    // Return the genesis block.
1134    Ok(block)
1135}
1136
1137// Resolve socket addresses (not URLs) in a host:port format compliant with C::getaddrinfo.
1138fn resolve_potential_hostnames(ip_or_hostname: &str) -> Result<SocketAddr> {
1139    let trimmed = ip_or_hostname.trim();
1140    // Perform some basic validity checks.
1141    if !trimmed.contains(':') {
1142        bail!(
1143            "The supplied trusted hostname or IP ('{trimmed}') is malformed: missing colon separating the host from the port"
1144        );
1145    }
1146    if trimmed.contains("://") {
1147        bail!("The supplied trusted hostname or IP ('{trimmed}') is malformed: URLs are not supported");
1148    }
1149    match trimmed.to_socket_addrs() {
1150        Ok(mut ip_iter) => {
1151            // A hostname might resolve to multiple IP addresses. We will use only the first one,
1152            // assuming this aligns with the user's expectations.
1153            let Some(ip) = ip_iter.next() else {
1154                bail!("The supplied trusted hostname ('{trimmed}') does not reference any ip.");
1155            };
1156            Ok(ip)
1157        }
1158        Err(e) => Err(anyhow!("The supplied trusted hostname or IP ('{trimmed}') is malformed: {e}")),
1159    }
1160}
1161
1162#[cfg(test)]
1163mod tests {
1164    use super::*;
1165    use crate::commands::{CLI, Command};
1166    use snarkvm::prelude::MainnetV0;
1167
1168    use ureq::http;
1169
1170    type CurrentNetwork = MainnetV0;
1171
1172    #[test]
1173    fn test_parse_trusted_addrs() {
1174        let config = Start::try_parse_from(["snarkos", "--peers", ""].iter()).unwrap();
1175        assert!(config.parse_trusted_addrs(&config.peers).is_ok());
1176        assert!(config.parse_trusted_addrs(&config.peers).unwrap().is_empty());
1177
1178        let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5"].iter()).unwrap();
1179        assert!(config.parse_trusted_addrs(&config.peers).is_ok());
1180        assert_eq!(config.parse_trusted_addrs(&config.peers).unwrap(), vec![
1181            SocketAddr::from_str("1.2.3.4:5").unwrap()
1182        ]);
1183
1184        let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
1185        assert!(config.parse_trusted_addrs(&config.peers).is_ok());
1186        assert_eq!(config.parse_trusted_addrs(&config.peers).unwrap(), vec![
1187            SocketAddr::from_str("1.2.3.4:5").unwrap(),
1188            SocketAddr::from_str("6.7.8.9:0").unwrap()
1189        ]);
1190    }
1191
1192    #[test]
1193    fn test_parse_trusted_validators() {
1194        let config = Start::try_parse_from(["snarkos", "--validators", ""].iter()).unwrap();
1195        assert!(config.parse_trusted_addrs(&config.validators).is_ok());
1196        assert!(config.parse_trusted_addrs(&config.validators).unwrap().is_empty());
1197
1198        let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5"].iter()).unwrap();
1199        assert!(config.parse_trusted_addrs(&config.validators).is_ok());
1200        assert_eq!(config.parse_trusted_addrs(&config.validators).unwrap(), vec![
1201            SocketAddr::from_str("1.2.3.4:5").unwrap()
1202        ]);
1203
1204        let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
1205        assert!(config.parse_trusted_addrs(&config.validators).is_ok());
1206        assert_eq!(config.parse_trusted_addrs(&config.validators).unwrap(), vec![
1207            SocketAddr::from_str("1.2.3.4:5").unwrap(),
1208            SocketAddr::from_str("6.7.8.9:0").unwrap()
1209        ]);
1210    }
1211
1212    #[test]
1213    fn test_parse_log_filter() {
1214        // Ensure we cannot set, both, log-filter and verbosity
1215        let result = Start::try_parse_from(["snarkos", "--verbosity=5", "--log-filter=warn"].iter());
1216        assert!(result.is_err(), "Must not be able to set log-filter and verbosity at the same time");
1217
1218        // Ensure the values are set correctly.
1219        let config = Start::try_parse_from(["snarkos", "--verbosity=5"].iter()).unwrap();
1220        assert_eq!(config.verbosity, 5);
1221        let config = Start::try_parse_from(["snarkos", "--log-filter=snarkos=warn"].iter()).unwrap();
1222        assert_eq!(config.log_filter, Some("snarkos=warn".to_string()));
1223    }
1224
1225    #[test]
1226    fn test_parse_cdn() -> Result<()> {
1227        // Validator (Prod)
1228        let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
1229        assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1230        let config =
1231            Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter())
1232                .unwrap();
1233        assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1234        let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--nocdn"].iter())?;
1235        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1236
1237        // Validator (Dev)
1238        let config =
1239            Start::try_parse_from(["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
1240        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1241        let config = Start::try_parse_from(
1242            ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1243        )
1244        .unwrap();
1245        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1246        let config = Start::try_parse_from(
1247            ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--nocdn"].iter(),
1248        )?;
1249        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1250
1251        // Prover (Prod)
1252        let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx"].iter())?;
1253        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1254        let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter())?;
1255        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1256        let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--nocdn"].iter())?;
1257        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1258
1259        // Prover (Dev)
1260        let config =
1261            Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
1262        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1263        let config = Start::try_parse_from(
1264            ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1265        )
1266        .unwrap();
1267        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1268        let config =
1269            Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--nocdn"].iter())
1270                .unwrap();
1271        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1272
1273        // Client (Prod)
1274        let config = Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
1275        assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1276        let config =
1277            Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
1278        assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1279        let config =
1280            Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--nocdn"].iter()).unwrap();
1281        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1282
1283        // Client (Dev)
1284        let config =
1285            Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
1286        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1287        let config = Start::try_parse_from(
1288            ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1289        )
1290        .unwrap();
1291        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1292        let config =
1293            Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--nocdn"].iter())
1294                .unwrap();
1295        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1296
1297        // Default (Prod)
1298        let config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1299        assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1300        let config = Start::try_parse_from(["snarkos", "--cdn", "url"].iter()).unwrap();
1301        assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1302        let config = Start::try_parse_from(["snarkos", "--nocdn"].iter()).unwrap();
1303        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1304
1305        // Default (Dev)
1306        let config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
1307        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1308        let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", "url"].iter()).unwrap();
1309        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1310        let config = Start::try_parse_from(["snarkos", "--dev", "0", "--nocdn"].iter()).unwrap();
1311        assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1312
1313        Ok(())
1314    }
1315
1316    #[test]
1317    fn test_parse_development_and_genesis() {
1318        let prod_genesis = Block::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap();
1319
1320        let mut trusted_peers = vec![];
1321        let mut trusted_validators = vec![];
1322        let mut config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1323        config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1324        let candidate_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1325        assert_eq!(trusted_peers.len(), 0);
1326        assert_eq!(trusted_validators.len(), 0);
1327        assert_eq!(candidate_genesis, prod_genesis);
1328
1329        let _config = Start::try_parse_from(["snarkos", "--dev", ""].iter()).unwrap_err();
1330
1331        // Validator dev mode with default settings.
1332        let mut trusted_peers = vec![];
1333        let mut trusted_validators = vec![];
1334        let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--validator"].iter()).unwrap();
1335        config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1336        assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1337        assert_eq!(trusted_validators.len(), 3);
1338
1339        // Validator dev mode with `--rest` flag.
1340        let mut trusted_peers = vec![];
1341        let mut trusted_validators = vec![];
1342        let mut config =
1343            Start::try_parse_from(["snarkos", "--dev", "1", "--rest", "127.0.0.1:8080", "--validator"].iter()).unwrap();
1344        config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1345        assert_eq!(config.rest, Some(SocketAddr::from_str("127.0.0.1:8080").unwrap()));
1346        assert_eq!(trusted_validators.len(), 3);
1347
1348        // Validator dev mode with `--norest` flag.
1349        let mut trusted_peers = vec![];
1350        let mut trusted_validators = vec![];
1351        let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--norest", "--validator"].iter()).unwrap();
1352        config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1353        assert!(config.rest.is_none());
1354        assert_eq!(trusted_validators.len(), 3);
1355
1356        // Client dev node.
1357        let mut trusted_peers = vec![];
1358        let mut trusted_validators = vec![];
1359        let mut config = Start::try_parse_from(["snarkos", "--dev", "5"].iter()).unwrap();
1360        config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1361        let expected_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1362        assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4135").unwrap()));
1363        assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3035").unwrap()));
1364        assert_eq!(trusted_peers.len(), 2);
1365        assert_eq!(trusted_validators.len(), 0);
1366        assert!(!config.validator);
1367        assert!(!config.prover);
1368        assert!(!config.client);
1369        assert_ne!(expected_genesis, prod_genesis);
1370
1371        // Validator dev node with `--private-key` flag.
1372        let mut trusted_peers = vec![];
1373        let mut trusted_validators = vec![];
1374        let mut config =
1375            Start::try_parse_from(["snarkos", "--dev", "1", "--validator", "--private-key", ""].iter()).unwrap();
1376        config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1377        let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1378        assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4131").unwrap()));
1379        assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1380        assert_eq!(trusted_peers.len(), 0);
1381        assert_eq!(trusted_validators.len(), 3);
1382        assert!(config.validator);
1383        assert!(!config.prover);
1384        assert!(!config.client);
1385        assert_eq!(genesis, expected_genesis);
1386
1387        // Prover dev node with `--private-key` flag.
1388        let mut trusted_peers = vec![];
1389        let mut trusted_validators = vec![];
1390        let mut config =
1391            Start::try_parse_from(["snarkos", "--dev", "6", "--prover", "--private-key", ""].iter()).unwrap();
1392        config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1393        let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1394        assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4136").unwrap()));
1395        assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3036").unwrap()));
1396        assert_eq!(trusted_peers.len(), 2);
1397        assert_eq!(trusted_validators.len(), 0);
1398        assert!(!config.validator);
1399        assert!(config.prover);
1400        assert!(!config.client);
1401        assert_eq!(genesis, expected_genesis);
1402
1403        // Client dev node with `--private-key` flag.
1404        let mut trusted_peers = vec![];
1405        let mut trusted_validators = vec![];
1406        let mut config =
1407            Start::try_parse_from(["snarkos", "--dev", "10", "--client", "--private-key", ""].iter()).unwrap();
1408        config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1409        let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1410        assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4140").unwrap()));
1411        assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3040").unwrap()));
1412        assert_eq!(trusted_peers.len(), 2);
1413        assert_eq!(trusted_validators.len(), 0);
1414        assert!(!config.validator);
1415        assert!(!config.prover);
1416        assert!(config.client);
1417        assert_eq!(genesis, expected_genesis);
1418    }
1419
1420    /// Tests that you cannot pass the `--dev-num-clients` flag while also passing the `--peers` flag.
1421    #[test]
1422    fn test_parse_development_num_clients_and_peers() {
1423        let result = Start::try_parse_from(
1424            ["snarkos", "--validator", "--dev", "1", "--peers", "127.0.0.1:3030", "--dev-num-clients", "1"].iter(),
1425        );
1426        assert!(result.is_err());
1427    }
1428
1429    #[test]
1430    fn clap_snarkos_start() {
1431        let arg_vec = vec![
1432            "snarkos",
1433            "start",
1434            "--nodisplay",
1435            "--dev",
1436            "2",
1437            "--validator",
1438            "--private-key",
1439            "PRIVATE_KEY",
1440            "--cdn",
1441            "CDN",
1442            "--peers",
1443            "IP1,IP2,IP3",
1444            "--validators",
1445            "IP1,IP2,IP3",
1446            "--rest",
1447            "127.0.0.1:3030",
1448        ];
1449        let cli = CLI::parse_from(arg_vec);
1450
1451        let Command::Start(start) = cli.command else {
1452            panic!("Unexpected result of clap parsing!");
1453        };
1454
1455        assert!(start.nodisplay);
1456        assert_eq!(start.dev, Some(2));
1457        assert!(start.validator);
1458        assert_eq!(start.private_key.as_deref(), Some("PRIVATE_KEY"));
1459        assert_eq!(start.cdn, Some(http::Uri::try_from("CDN").unwrap()));
1460        assert_eq!(start.rest, Some("127.0.0.1:3030".parse().unwrap()));
1461        assert_eq!(start.network, 0);
1462        assert_eq!(start.peers, Some("IP1,IP2,IP3".to_string()));
1463        assert_eq!(start.validators, Some("IP1,IP2,IP3".to_string()));
1464    }
1465
1466    /// Ensure two clients do not connect to the same validators.
1467    #[test]
1468    fn test_parse_development_client_validators() {
1469        let mut client1_config =
1470            Start::try_parse_from(["snarkos", "--dev", "10", "--client", "--private-key", ""].iter()).unwrap();
1471        let mut trusted_peers1 = vec![];
1472        let mut trusted_validators1 = vec![];
1473        client1_config.parse_development(&mut trusted_peers1, &mut trusted_validators1).unwrap();
1474
1475        let mut client2_config =
1476            Start::try_parse_from(["snarkos", "--dev", "11", "--client", "--private-key", ""].iter()).unwrap();
1477        let mut trusted_peers2 = vec![];
1478        let mut trusted_validators2 = vec![];
1479        client2_config.parse_development(&mut trusted_peers2, &mut trusted_validators2).unwrap();
1480
1481        assert_ne!(trusted_peers1, trusted_peers2);
1482    }
1483
1484    #[test]
1485    fn parse_peers_when_ips() {
1486        let arg_vec = vec!["snarkos", "start", "--peers", "127.0.0.1:3030,127.0.0.2:3030"];
1487        let cli = CLI::parse_from(arg_vec);
1488
1489        if let Command::Start(start) = cli.command {
1490            let peers = start.parse_trusted_addrs(&start.peers);
1491            assert!(peers.is_ok());
1492            assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1493        } else {
1494            panic!("Unexpected result of clap parsing!");
1495        }
1496    }
1497
1498    #[test]
1499    fn parse_peers_when_hostnames() {
1500        let arg_vec = vec!["snarkos", "start", "--peers", "www.example.com:4130,www.google.com:4130"];
1501        let cli = CLI::parse_from(arg_vec);
1502
1503        if let Command::Start(start) = cli.command {
1504            let peers = start.parse_trusted_addrs(&start.peers);
1505            assert!(peers.is_ok());
1506            assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1507        } else {
1508            panic!("Unexpected result of clap parsing!");
1509        }
1510    }
1511
1512    #[test]
1513    fn parse_peers_when_mixed_and_with_whitespaces() {
1514        let arg_vec = vec!["snarkos", "start", "--peers", "  127.0.0.1:3030,  www.google.com:4130 "];
1515        let cli = CLI::parse_from(arg_vec);
1516
1517        if let Command::Start(start) = cli.command {
1518            let peers = start.parse_trusted_addrs(&start.peers);
1519            assert!(peers.is_ok());
1520            assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1521        } else {
1522            panic!("Unexpected result of clap parsing!");
1523        }
1524    }
1525
1526    #[test]
1527    fn parse_peers_when_unknown_hostname_gracefully() {
1528        let arg_vec = vec!["snarkos", "start", "--peers", "banana.cake.eafafdaeefasdfasd.com"];
1529        let cli = CLI::parse_from(arg_vec);
1530
1531        if let Command::Start(start) = cli.command {
1532            assert!(start.parse_trusted_addrs(&start.peers).is_err());
1533        } else {
1534            panic!("Unexpected result of clap parsing!");
1535        }
1536    }
1537}