use snarkos_account::Account;
use snarkos_display::Display;
use snarkos_node::{bft::MEMORY_POOL_PORT, router::messages::NodeType, Node};
use snarkvm::{
console::{
account::{Address, PrivateKey},
algorithms::Hash,
network::{CanaryV0, MainnetV0, Network, TestnetV0},
},
ledger::{
block::Block,
committee::{Committee, MIN_DELEGATOR_STAKE, MIN_VALIDATOR_STAKE},
store::{helpers::memory::ConsensusMemory, ConsensusStore},
},
prelude::{FromBytes, ToBits, ToBytes},
synthesizer::VM,
utilities::to_bytes_le,
};
use aleo_std::StorageMode;
use anyhow::{bail, ensure, Result};
use clap::Parser;
use colored::Colorize;
use core::str::FromStr;
use indexmap::IndexMap;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaChaRng;
use serde::{Deserialize, Serialize};
use std::{
net::SocketAddr,
path::PathBuf,
sync::{atomic::AtomicBool, Arc},
};
use tokio::runtime::{self, Runtime};
#[cfg(target_family = "unix")]
const RECOMMENDED_MIN_NOFILES_LIMIT: u64 = 2048;
const DEVELOPMENT_MODE_RNG_SEED: u64 = 1234567890u64;
const DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS: u16 = 4;
const CDN_BASE_URL: &str = "https://blocks.aleo.org";
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct BondedBalances(IndexMap<String, (String, String, u64)>);
impl FromStr for BondedBalances {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_str(s)
}
}
#[derive(Clone, Debug, Parser)]
pub struct Start {
#[clap(default_value = "0", long = "network")]
pub network: u16,
#[clap(long = "validator")]
pub validator: bool,
#[clap(long = "prover")]
pub prover: bool,
#[clap(long = "client")]
pub client: bool,
#[clap(long = "private-key")]
pub private_key: Option<String>,
#[clap(long = "private-key-file")]
pub private_key_file: Option<PathBuf>,
#[clap(long = "node")]
pub node: Option<SocketAddr>,
#[clap(long = "bft")]
pub bft: Option<SocketAddr>,
#[clap(default_value = "", long = "peers")]
pub peers: String,
#[clap(default_value = "", long = "validators")]
pub validators: String,
#[clap(long = "allow-external-peers")]
pub allow_external_peers: bool,
#[clap(long = "rest")]
pub rest: Option<SocketAddr>,
#[clap(default_value = "10", long = "rest-rps")]
pub rest_rps: u32,
#[clap(long)]
pub norest: bool,
#[clap(long)]
pub nodisplay: bool,
#[clap(default_value = "1", long = "verbosity")]
pub verbosity: u8,
#[clap(default_value_os_t = std::env::temp_dir().join("snarkos.log"), long = "logfile")]
pub logfile: PathBuf,
#[clap(default_value = "false", long = "metrics")]
pub metrics: bool,
#[clap(long = "storage")]
pub storage: Option<PathBuf>,
#[clap(long = "cdn")]
pub cdn: Option<String>,
#[clap(long)]
pub nocdn: bool,
#[clap(long)]
pub dev: Option<u16>,
#[clap(long)]
pub dev_num_validators: Option<u16>,
#[clap(default_value = "false", long = "no-dev-txs")]
pub no_dev_txs: bool,
#[clap(long)]
pub dev_bonded_balances: Option<BondedBalances>,
}
impl Start {
pub fn parse(self) -> Result<String> {
let shutdown: Arc<AtomicBool> = Default::default();
let log_receiver =
crate::helpers::initialize_logger(self.verbosity, self.nodisplay, self.logfile.clone(), shutdown.clone());
Self::runtime().block_on(async move {
let mut cli = self.clone();
match cli.network {
MainnetV0::ID => {
let node = cli.parse_node::<MainnetV0>(shutdown.clone()).await.expect("Failed to parse the node");
if !cli.nodisplay {
Display::start(node, log_receiver).expect("Failed to initialize the display");
}
}
TestnetV0::ID => {
let node = cli.parse_node::<TestnetV0>(shutdown.clone()).await.expect("Failed to parse the node");
if !cli.nodisplay {
Display::start(node, log_receiver).expect("Failed to initialize the display");
}
}
CanaryV0::ID => {
let node = cli.parse_node::<CanaryV0>(shutdown.clone()).await.expect("Failed to parse the node");
if !cli.nodisplay {
Display::start(node, log_receiver).expect("Failed to initialize the display");
}
}
_ => panic!("Invalid network ID specified"),
};
std::future::pending::<()>().await;
});
Ok(String::new())
}
}
impl Start {
fn parse_trusted_peers(&self) -> Result<Vec<SocketAddr>> {
match self.peers.is_empty() {
true => Ok(vec![]),
false => Ok(self
.peers
.split(',')
.flat_map(|ip| match ip.parse::<SocketAddr>() {
Ok(ip) => Some(ip),
Err(e) => {
eprintln!("The IP supplied to --peers ('{ip}') is malformed: {e}");
None
}
})
.collect()),
}
}
fn parse_trusted_validators(&self) -> Result<Vec<SocketAddr>> {
match self.validators.is_empty() {
true => Ok(vec![]),
false => Ok(self
.validators
.split(',')
.flat_map(|ip| match ip.parse::<SocketAddr>() {
Ok(ip) => Some(ip),
Err(e) => {
eprintln!("The IP supplied to --validators ('{ip}') is malformed: {e}");
None
}
})
.collect()),
}
}
fn parse_cdn<N: Network>(&self) -> Option<String> {
let is_no_node_type = !(self.validator || self.prover || self.client);
if self.dev.is_some() || self.nocdn || self.prover || is_no_node_type {
None
}
else {
match &self.cdn {
Some(cdn) => match cdn.is_empty() {
true => None,
false => Some(cdn.clone()),
},
None => match N::ID {
MainnetV0::ID => Some(format!("{CDN_BASE_URL}/mainnet/v0")),
TestnetV0::ID => Some(format!("{CDN_BASE_URL}/testnet/v0")),
CanaryV0::ID => Some(format!("{CDN_BASE_URL}/canary/v0")),
_ => None,
},
}
}
}
fn parse_private_key<N: Network>(&self) -> Result<Account<N>> {
match self.dev {
None => match (&self.private_key, &self.private_key_file) {
(Some(private_key), None) => Account::from_str(private_key.trim()),
(None, Some(path)) => {
check_permissions(path)?;
Account::from_str(std::fs::read_to_string(path)?.trim())
}
(None, None) => match self.client {
true => Account::new(&mut rand::thread_rng()),
false => bail!("Missing the '--private-key' or '--private-key-file' argument"),
},
(Some(_), Some(_)) => {
bail!("Cannot use '--private-key' and '--private-key-file' simultaneously, please use only one")
}
},
Some(dev) => {
Account::try_from({
let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
for _ in 0..dev {
let _ = PrivateKey::<N>::new(&mut rng)?;
}
let private_key = PrivateKey::<N>::new(&mut rng)?;
println!("🔑 Your development private key for node {dev} is {}.\n", private_key.to_string().bold());
private_key
})
}
}
}
fn parse_development(
&mut self,
trusted_peers: &mut Vec<SocketAddr>,
trusted_validators: &mut Vec<SocketAddr>,
) -> Result<()> {
if let Some(dev) = self.dev {
if trusted_peers.is_empty() {
for i in 0..dev {
if i != dev {
trusted_peers.push(SocketAddr::from_str(&format!("127.0.0.1:{}", 4130 + i))?);
}
}
}
if trusted_validators.is_empty() {
for i in 0..2 {
if i != dev {
trusted_validators.push(SocketAddr::from_str(&format!("127.0.0.1:{}", MEMORY_POOL_PORT + i))?);
}
}
}
if self.node.is_none() {
self.node = Some(SocketAddr::from_str(&format!("0.0.0.0:{}", 4130 + dev))?);
}
if !self.norest && self.rest.is_none() {
self.rest = Some(SocketAddr::from_str(&format!("0.0.0.0:{}", 3030 + dev)).unwrap());
}
}
Ok(())
}
fn parse_genesis<N: Network>(&self) -> Result<Block<N>> {
if self.dev.is_some() {
let num_committee_members = match self.dev_num_validators {
Some(num_committee_members) => num_committee_members,
None => DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
};
ensure!(
num_committee_members >= DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
"Number of genesis committee members is too low"
);
let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
let development_private_keys =
(0..num_committee_members).map(|_| PrivateKey::<N>::new(&mut rng)).collect::<Result<Vec<_>>>()?;
let development_addresses =
development_private_keys.iter().map(Address::<N>::try_from).collect::<Result<Vec<_>>>()?;
let (committee, bonded_balances) = match &self.dev_bonded_balances {
Some(bonded_balances) => {
let bonded_balances = bonded_balances
.0
.iter()
.map(|(staker_address, (validator_address, withdrawal_address, amount))| {
let staker_addr = Address::<N>::from_str(staker_address)?;
let validator_addr = Address::<N>::from_str(validator_address)?;
let withdrawal_addr = Address::<N>::from_str(withdrawal_address)?;
Ok((staker_addr, (validator_addr, withdrawal_addr, *amount)))
})
.collect::<Result<IndexMap<_, _>>>()?;
let mut members = IndexMap::new();
for (staker_address, (validator_address, _, amount)) in bonded_balances.iter() {
match staker_address == validator_address {
true => ensure!(amount >= &MIN_VALIDATOR_STAKE, "Validator stake is too low"),
false => ensure!(amount >= &MIN_DELEGATOR_STAKE, "Delegator stake is too low"),
}
ensure!(
development_addresses.contains(validator_address),
"Validator address {validator_address} is not included in the list of development addresses"
);
members.entry(*validator_address).and_modify(|(stake, _, _)| *stake += amount).or_insert((
*amount,
true,
rng.gen_range(0..100),
));
}
let committee = Committee::<N>::new(0u64, members)?;
(committee, bonded_balances)
}
None => {
let stake_per_member =
N::STARTING_SUPPLY.saturating_div(2).saturating_div(num_committee_members as u64);
ensure!(stake_per_member >= MIN_VALIDATOR_STAKE, "Committee stake per member is too low");
let members = development_addresses
.iter()
.map(|address| (*address, (stake_per_member, true, rng.gen_range(0..100))))
.collect::<IndexMap<_, _>>();
let bonded_balances = members
.iter()
.map(|(address, (stake, _, _))| (*address, (*address, *address, *stake)))
.collect::<IndexMap<_, _>>();
let committee = Committee::<N>::new(0u64, members)?;
(committee, bonded_balances)
}
};
ensure!(
committee.members().len() == num_committee_members as usize,
"Number of committee members {} does not match the expected number of members {num_committee_members}",
committee.members().len()
);
let remaining_balance = N::STARTING_SUPPLY.saturating_sub(committee.total_stake());
let public_balance_per_validator = remaining_balance.saturating_div(num_committee_members as u64);
let mut public_balances = development_private_keys
.iter()
.map(|private_key| Ok((Address::try_from(private_key)?, public_balance_per_validator)))
.collect::<Result<indexmap::IndexMap<_, _>>>()?;
let leftover =
remaining_balance.saturating_sub(public_balance_per_validator * num_committee_members as u64);
if leftover > 0 {
let (_, balance) = public_balances.get_index_mut(0).unwrap();
*balance += leftover;
}
let public_balances_sum: u64 = public_balances.values().copied().sum();
if committee.total_stake() + public_balances_sum != N::STARTING_SUPPLY {
bail!("Sum of committee stakes and public balances does not equal total starting supply.");
}
load_or_compute_genesis(development_private_keys[0], committee, public_balances, bonded_balances, &mut rng)
} else {
if self.dev_num_validators.is_some() {
eprintln!("The '--dev-num-validators' flag is ignored because '--dev' is not set");
}
Block::from_bytes_le(N::genesis_bytes())
}
}
const fn parse_node_type(&self) -> NodeType {
if self.validator {
NodeType::Validator
} else if self.prover {
NodeType::Prover
} else {
NodeType::Client
}
}
#[rustfmt::skip]
async fn parse_node<N: Network>(&mut self, shutdown: Arc<AtomicBool>) -> Result<Node<N>> {
println!("{}", crate::helpers::welcome_message());
let mut trusted_peers = self.parse_trusted_peers()?;
let mut trusted_validators = self.parse_trusted_validators()?;
self.parse_development(&mut trusted_peers, &mut trusted_validators)?;
let cdn = self.parse_cdn::<N>();
let genesis = self.parse_genesis::<N>()?;
let account = self.parse_private_key::<N>()?;
let node_type = self.parse_node_type();
let node_ip = match self.node {
Some(node_ip) => node_ip,
None => SocketAddr::from_str("0.0.0.0:4130").unwrap(),
};
let rest_ip = match self.norest {
true => None,
false => self.rest.or_else(|| Some("0.0.0.0:3030".parse().unwrap())),
};
if self.nodisplay {
println!("👛 Your Aleo address is {}.\n", account.address().to_string().bold());
println!(
"🧭 Starting {} on {} at {}.\n",
node_type.description().bold(),
N::NAME.bold(),
node_ip.to_string().bold()
);
if node_type.is_validator() {
if let Some(rest_ip) = rest_ip {
println!("🌐 Starting the REST server at {}.\n", rest_ip.to_string().bold());
if let Ok(jwt_token) = snarkos_node_rest::Claims::new(account.address()).to_jwt_string() {
println!("🔑 Your one-time JWT token is {}\n", jwt_token.dimmed());
}
}
}
}
#[cfg(target_family = "unix")]
if node_type.is_validator() {
crate::helpers::check_open_files_limit(RECOMMENDED_MIN_NOFILES_LIMIT);
}
crate::helpers::check_validator_machine(node_type);
if self.metrics {
metrics::initialize_metrics();
}
let storage_mode = match &self.storage {
Some(path) => StorageMode::Custom(path.clone()),
None => StorageMode::from(self.dev),
};
let dev_txs = match self.dev {
Some(_) => !self.no_dev_txs,
None => {
if self.no_dev_txs {
eprintln!("The '--no-dev-txs' flag is ignored because '--dev' is not set");
}
false
}
};
match node_type {
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,
NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, storage_mode, shutdown.clone()).await,
NodeType::Client => Node::new_client(node_ip, rest_ip, self.rest_rps, account, &trusted_peers, genesis, cdn, storage_mode, shutdown).await,
}
}
fn runtime() -> Runtime {
let num_cores = num_cpus::get();
let (num_tokio_worker_threads, max_tokio_blocking_threads, num_rayon_cores_global) =
(2 * num_cores, 512, num_cores);
rayon::ThreadPoolBuilder::new()
.stack_size(8 * 1024 * 1024)
.num_threads(num_rayon_cores_global)
.build_global()
.unwrap();
runtime::Builder::new_multi_thread()
.enable_all()
.thread_stack_size(8 * 1024 * 1024)
.worker_threads(num_tokio_worker_threads)
.max_blocking_threads(max_tokio_blocking_threads)
.build()
.expect("Failed to initialize a runtime for the router")
}
}
fn check_permissions(path: &PathBuf) -> Result<(), snarkvm::prelude::Error> {
#[cfg(target_family = "unix")]
{
use std::os::unix::fs::PermissionsExt;
ensure!(path.exists(), "The file '{:?}' does not exist", path);
let parent = path.parent();
if let Some(parent) = parent {
let parent_permissions = parent.metadata()?.permissions().mode();
ensure!(
parent_permissions & 0o777 == 0o700,
"The folder {:?} must be readable only by the owner (0700)",
parent
);
}
let permissions = path.metadata()?.permissions().mode();
ensure!(permissions & 0o777 == 0o600, "The file {:?} must be readable only by the owner (0600)", path);
}
Ok(())
}
fn load_or_compute_genesis<N: Network>(
genesis_private_key: PrivateKey<N>,
committee: Committee<N>,
public_balances: indexmap::IndexMap<Address<N>, u64>,
bonded_balances: indexmap::IndexMap<Address<N>, (Address<N>, Address<N>, u64)>,
rng: &mut ChaChaRng,
) -> Result<Block<N>> {
let mut preimage = Vec::new();
preimage.extend(&N::ID.to_le_bytes());
preimage.extend(genesis_private_key.to_bytes_le()?);
preimage.extend(committee.to_bytes_le()?);
preimage.extend(&to_bytes_le![public_balances.iter().collect::<Vec<(_, _)>>()]?);
preimage.extend(&to_bytes_le![
bonded_balances
.iter()
.flat_map(|(staker, (validator, withdrawal, amount))| to_bytes_le![staker, validator, withdrawal, amount])
.collect::<Vec<_>>()
]?);
match N::ID {
snarkvm::console::network::MainnetV0::ID => {
preimage.extend(snarkvm::parameters::mainnet::BondValidatorVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::mainnet::BondPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::mainnet::UnbondPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::mainnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::mainnet::SetValidatorStateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::mainnet::TransferPrivateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::mainnet::TransferPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::mainnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::mainnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::mainnet::FeePrivateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::mainnet::FeePublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::mainnet::InclusionVerifier::METADATA.as_bytes());
}
snarkvm::console::network::TestnetV0::ID => {
preimage.extend(snarkvm::parameters::testnet::BondValidatorVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::testnet::BondPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::testnet::UnbondPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::testnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::testnet::SetValidatorStateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::testnet::TransferPrivateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::testnet::TransferPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::testnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::testnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::testnet::FeePrivateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::testnet::FeePublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::testnet::InclusionVerifier::METADATA.as_bytes());
}
snarkvm::console::network::CanaryV0::ID => {
preimage.extend(snarkvm::parameters::canary::BondValidatorVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::canary::BondPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::canary::UnbondPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::canary::ClaimUnbondPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::canary::SetValidatorStateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::canary::TransferPrivateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::canary::TransferPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::canary::TransferPrivateToPublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::canary::TransferPublicToPrivateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::canary::FeePrivateVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::canary::FeePublicVerifier::METADATA.as_bytes());
preimage.extend(snarkvm::parameters::canary::InclusionVerifier::METADATA.as_bytes());
}
_ => {
bail!("Unrecognized Network ID: {}", N::ID);
}
}
let hasher = snarkvm::console::algorithms::BHP256::<N>::setup("aleo.dev.block")?;
let hash = hasher.hash(&preimage.to_bits_le())?.to_string();
let load_block = |file_path| -> Result<Block<N>> {
let buffer = std::fs::read(file_path)?;
Block::from_bytes_le(&buffer)
};
let file_path = std::env::temp_dir().join(hash);
if file_path.exists() {
if let Ok(block) = load_block(&file_path) {
return Ok(block);
}
}
let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(Some(0))?)?;
let block = vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng)?;
std::fs::write(&file_path, block.to_bytes_le()?)?;
Ok(block)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::commands::{Command, CLI};
use snarkvm::prelude::MainnetV0;
type CurrentNetwork = MainnetV0;
#[test]
fn test_parse_trusted_peers() {
let config = Start::try_parse_from(["snarkos", "--peers", ""].iter()).unwrap();
assert!(config.parse_trusted_peers().is_ok());
assert!(config.parse_trusted_peers().unwrap().is_empty());
let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5"].iter()).unwrap();
assert!(config.parse_trusted_peers().is_ok());
assert_eq!(config.parse_trusted_peers().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
assert!(config.parse_trusted_peers().is_ok());
assert_eq!(config.parse_trusted_peers().unwrap(), vec![
SocketAddr::from_str("1.2.3.4:5").unwrap(),
SocketAddr::from_str("6.7.8.9:0").unwrap()
]);
}
#[test]
fn test_parse_trusted_validators() {
let config = Start::try_parse_from(["snarkos", "--validators", ""].iter()).unwrap();
assert!(config.parse_trusted_validators().is_ok());
assert!(config.parse_trusted_validators().unwrap().is_empty());
let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5"].iter()).unwrap();
assert!(config.parse_trusted_validators().is_ok());
assert_eq!(config.parse_trusted_validators().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
assert!(config.parse_trusted_validators().is_ok());
assert_eq!(config.parse_trusted_validators().unwrap(), vec![
SocketAddr::from_str("1.2.3.4:5").unwrap(),
SocketAddr::from_str("6.7.8.9:0").unwrap()
]);
}
#[test]
fn test_parse_cdn() {
let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_some());
let config =
Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter())
.unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_some());
let config =
Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config =
Start::try_parse_from(["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(
["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
)
.unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(
["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", ""].iter(),
)
.unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config =
Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config =
Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config =
Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(
["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
)
.unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(
["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", ""].iter(),
)
.unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_some());
let config =
Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_some());
let config =
Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config =
Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(
["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
)
.unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(
["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", ""].iter(),
)
.unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(["snarkos"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(["snarkos", "--cdn", "url"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(["snarkos", "--cdn", ""].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", "url"].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", ""].iter()).unwrap();
assert!(config.parse_cdn::<CurrentNetwork>().is_none());
}
#[test]
fn test_parse_development_and_genesis() {
let prod_genesis = Block::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap();
let mut trusted_peers = vec![];
let mut trusted_validators = vec![];
let mut config = Start::try_parse_from(["snarkos"].iter()).unwrap();
config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
let candidate_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
assert_eq!(trusted_peers.len(), 0);
assert_eq!(trusted_validators.len(), 0);
assert_eq!(candidate_genesis, prod_genesis);
let _config = Start::try_parse_from(["snarkos", "--dev", ""].iter()).unwrap_err();
let mut trusted_peers = vec![];
let mut trusted_validators = vec![];
let mut config = Start::try_parse_from(["snarkos", "--dev", "1"].iter()).unwrap();
config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
let mut trusted_peers = vec![];
let mut trusted_validators = vec![];
let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--rest", "127.0.0.1:8080"].iter()).unwrap();
config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
assert_eq!(config.rest, Some(SocketAddr::from_str("127.0.0.1:8080").unwrap()));
let mut trusted_peers = vec![];
let mut trusted_validators = vec![];
let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--norest"].iter()).unwrap();
config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
assert!(config.rest.is_none());
let mut trusted_peers = vec![];
let mut trusted_validators = vec![];
let mut config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
let expected_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4130").unwrap()));
assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3030").unwrap()));
assert_eq!(trusted_peers.len(), 0);
assert_eq!(trusted_validators.len(), 1);
assert!(!config.validator);
assert!(!config.prover);
assert!(!config.client);
assert_ne!(expected_genesis, prod_genesis);
let mut trusted_peers = vec![];
let mut trusted_validators = vec![];
let mut config =
Start::try_parse_from(["snarkos", "--dev", "1", "--validator", "--private-key", ""].iter()).unwrap();
config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4131").unwrap()));
assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
assert_eq!(trusted_peers.len(), 1);
assert_eq!(trusted_validators.len(), 1);
assert!(config.validator);
assert!(!config.prover);
assert!(!config.client);
assert_eq!(genesis, expected_genesis);
let mut trusted_peers = vec![];
let mut trusted_validators = vec![];
let mut config =
Start::try_parse_from(["snarkos", "--dev", "2", "--prover", "--private-key", ""].iter()).unwrap();
config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4132").unwrap()));
assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3032").unwrap()));
assert_eq!(trusted_peers.len(), 2);
assert_eq!(trusted_validators.len(), 2);
assert!(!config.validator);
assert!(config.prover);
assert!(!config.client);
assert_eq!(genesis, expected_genesis);
let mut trusted_peers = vec![];
let mut trusted_validators = vec![];
let mut config =
Start::try_parse_from(["snarkos", "--dev", "3", "--client", "--private-key", ""].iter()).unwrap();
config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4133").unwrap()));
assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3033").unwrap()));
assert_eq!(trusted_peers.len(), 3);
assert_eq!(trusted_validators.len(), 2);
assert!(!config.validator);
assert!(!config.prover);
assert!(config.client);
assert_eq!(genesis, expected_genesis);
}
#[test]
fn clap_snarkos_start() {
let arg_vec = vec![
"snarkos",
"start",
"--nodisplay",
"--dev",
"2",
"--validator",
"--private-key",
"PRIVATE_KEY",
"--cdn",
"CDN",
"--peers",
"IP1,IP2,IP3",
"--validators",
"IP1,IP2,IP3",
"--rest",
"127.0.0.1:3030",
];
let cli = CLI::parse_from(arg_vec);
if let Command::Start(start) = cli.command {
assert!(start.nodisplay);
assert_eq!(start.dev, Some(2));
assert!(start.validator);
assert_eq!(start.private_key.as_deref(), Some("PRIVATE_KEY"));
assert_eq!(start.cdn, Some("CDN".to_string()));
assert_eq!(start.rest, Some("127.0.0.1:3030".parse().unwrap()));
assert_eq!(start.network, 0);
assert_eq!(start.peers, "IP1,IP2,IP3");
assert_eq!(start.validators, "IP1,IP2,IP3");
} else {
panic!("Unexpected result of clap parsing!");
}
}
}