| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680 |
- pub mod mining;
- use std::error::Error;
- use std::fs;
- use std::path::Path;
- use clap::{arg, command, ArgAction, Parser};
- use libp2p::identity::Keypair;
- use libp2p::multiaddr::Multiaddr;
- use libp2p::{allow_block_list, connection_limits, memory_connection_limits, PeerId};
- use nockapp::driver::Operation;
- use nockapp::kernel::boot;
- use nockapp::wire::Wire;
- use nockapp::{one_punch_driver, NockApp, NounExt};
- use nockchain_bitcoin_sync::{bitcoin_watcher_driver, BitcoinRPCConnection, GenesisNodeType};
- use nockchain_libp2p_io::p2p::{
- MAX_ESTABLISHED_CONNECTIONS, MAX_ESTABLISHED_CONNECTIONS_PER_PEER,
- MAX_ESTABLISHED_INCOMING_CONNECTIONS, MAX_ESTABLISHED_OUTGOING_CONNECTIONS,
- MAX_PENDING_INCOMING_CONNECTIONS, MAX_PENDING_OUTGOING_CONNECTIONS,
- };
- use termcolor::{ColorChoice, StandardStream};
- use tokio::net::UnixListener;
- pub mod colors;
- use std::path::PathBuf;
- use clap::value_parser;
- use colors::*;
- use nockapp::noun::slab::NounSlab;
- use nockvm::jets::hot::HotEntry;
- use nockvm::noun::{D, T};
- use nockvm_macros::tas;
- use tracing::{debug, info, instrument};
- use crate::mining::MiningKeyConfig;
- /// Module for handling driver initialization signals
- pub mod driver_init {
- use nockapp::driver::{make_driver, IODriverFn, PokeResult};
- use nockapp::noun::slab::NounSlab;
- use nockapp::wire::{SystemWire, Wire};
- use nockvm::noun::{D, T};
- use nockvm_macros::tas;
- use tokio::sync::oneshot;
- use tracing::{debug, error, info};
- /// A collection of initialization signals for drivers
- #[derive(Default)]
- pub struct DriverInitSignals {
- /// Sender for the born signal
- pub born_tx: Option<oneshot::Sender<()>>,
- /// Receiver for the born signal
- pub born_rx: Option<oneshot::Receiver<()>>,
- /// Map of driver names to their initialization signal senders
- pub driver_signals: std::collections::HashMap<String, oneshot::Receiver<()>>,
- }
- impl DriverInitSignals {
- /// Create a new DriverInitSignals instance
- pub fn new() -> Self {
- let (born_tx, born_rx) = oneshot::channel();
- Self {
- born_tx: Some(born_tx),
- born_rx: Some(born_rx),
- driver_signals: std::collections::HashMap::new(),
- }
- }
- /// Register a driver with an initialization signal
- pub fn register_driver(&mut self, name: &str) -> oneshot::Sender<()> {
- let (tx, rx) = oneshot::channel();
- self.driver_signals.insert(name.to_string(), rx);
- tx
- }
- /// Get the initialization signal sender for a driver
- pub fn get_signal_sender(&self, name: &str) -> Option<&oneshot::Receiver<()>> {
- self.driver_signals.get(name)
- }
- /// Create a task that waits for all registered drivers to initialize
- pub fn create_born_task(&mut self) -> tokio::task::JoinHandle<()> {
- let born_tx = self.born_tx.take().expect("Born signal already used");
- let driver_signals = std::mem::take(&mut self.driver_signals);
- tokio::spawn(async move {
- // Wait for all registered drivers to initialize concurrently
- let mut join_set = tokio::task::JoinSet::new();
- for (name, rx) in driver_signals {
- let name = name.clone();
- join_set.spawn(async move {
- let _ = rx.await;
- info!("driver '{}' initialized", name);
- });
- }
- // Wait for all tasks to complete
- while let Some(result) = join_set.join_next().await {
- result.expect("Task panicked");
- }
- // Send the born poke signal
- let _ = born_tx.send(());
- info!("all drivers initialized, born poke sent");
- })
- }
- /// Create the born driver that waits for the born signal
- pub fn create_born_driver(&mut self) -> IODriverFn {
- let born_rx = self.born_rx.take().expect("born signal already used");
- make_driver(move |handle| {
- Box::pin(async move {
- // Wait for the born signal
- let _ = born_rx.await;
- // Send the born poke
- let mut born_slab = NounSlab::new();
- let born = T(
- &mut born_slab,
- &[D(tas!(b"command")), D(tas!(b"born")), D(0)],
- );
- born_slab.set_root(born);
- let wire = SystemWire.to_wire();
- let result = handle.poke(wire, born_slab).await?;
- match result {
- PokeResult::Ack => debug!("born poke acknowledged"),
- PokeResult::Nack => error!("Born poke nacked"),
- }
- Ok(())
- })
- })
- }
- }
- }
- // TODO: command-line/configure
- /** Path to read current node's identity from */
- pub const IDENTITY_PATH: &str = ".nockchain_identity";
- /** Path to read current node's peer ID from */
- pub const PEER_ID_EXTENSION: &str = ".peer_id";
- // TODO: command-line/configure
- /** Extension for peer ID files */
- const PEER_ID_FILE_EXTENSION: &str = "peerid";
- // Libp2p multiaddrs don't support const construction, so we have to put strings literals and parse them at startup
- /** Backbone nodes for our testnet */
- const TESTNET_BACKBONE_NODES: &[&str] = &[];
- // Libp2p multiaddrs don't support const construction, so we have to put strings literals and parse them at startup
- // TODO: feature flag testnet/realnet
- /** Backbone nodes for our realnet */
- #[allow(dead_code)]
- const REALNET_BACKBONE_NODES: &[&str] = &[
- "/dnsaddr/nockchain-backbone.zorp.io",
- "/ip4/34.35.75.234/udp/30000/quic-v1",
- "/ip4/34.176.41.23/udp/30000/quic-v1",
- "/ip4/34.16.237.144/udp/30000/quic-v1",
- "/ip4/34.85.34.153/udp/30000/quic-v1",
- "/ip4/34.95.155.151/udp/30000/quic-v1",
- "/ip4/34.97.242.48/udp/30000/quic-v1",
- "/ip4/34.162.206.28/udp/30000/quic-v1",
- "/ip4/34.174.22.166/udp/30000/quic-v1",
- "/ip4/34.129.248.106/udp/30000/quic-v1",
- "/ip4/34.18.98.38/udp/30000/quic-v1"
- ];
- /** How often we should affirmatively ask other nodes for their heaviest chain */
- const CHAIN_INTERVAL_SECS: u64 = 20;
- /// The height of the bitcoin block that we want to sync our genesis block to
- /// Currently, this is the height of an existing block for testing. It will be
- /// switched to a future block for launch.
- const GENESIS_HEIGHT: u64 = 897767;
- /// Command line arguments
- #[derive(Parser, Debug, Clone)]
- #[command(name = "nockchain")]
- pub struct NockchainCli {
- #[command(flatten)]
- pub nockapp_cli: nockapp::kernel::boot::Cli,
- #[arg(
- long,
- help = "npc socket path",
- default_value = ".socket/nockchain_npc.sock"
- )]
- pub npc_socket: String,
- #[arg(long, help = "Mine in-kernel", default_value = "false")]
- pub mine: bool,
- #[arg(
- long,
- help = "Pubkey to mine to (mutually exclusive with --mining-key-adv)"
- )]
- pub mining_pubkey: Option<String>,
- #[arg(
- long,
- help = "Advanced mining key configuration (mutually exclusive with --mining-pubkey). Format: share,m:key1,key2,key3",
- value_parser = value_parser!(MiningKeyConfig),
- num_args = 1..,
- value_delimiter = ',',
- )]
- pub mining_key_adv: Option<Vec<MiningKeyConfig>>,
- #[arg(long, help = "Watch for genesis block", default_value = "false")]
- pub genesis_watcher: bool,
- #[arg(long, help = "Mine genesis block", default_value = "false")]
- pub genesis_leader: bool,
- #[arg(long, help = "use fake genesis block", default_value = "false")]
- pub fakenet: bool,
- #[arg(long, help = "Genesis block message", default_value = "Hail Zorp")]
- pub genesis_message: String,
- #[arg(
- long,
- help = "URL for Bitcoin Core RPC",
- default_value = "http://100.98.183.39:8332"
- )]
- pub btc_node_url: String,
- #[arg(long, help = "Username for Bitcoin Core RPC")]
- pub btc_username: Option<String>,
- #[arg(long, help = "Password for Bitcoin Core RPC")]
- pub btc_password: Option<String>,
- #[arg(long, help = "Auth cookie path for Bitcoin Core RPC")]
- pub btc_auth_cookie: Option<String>,
- #[arg(long, short, help = "Initial peer", action = ArgAction::Append)]
- pub peer: Vec<String>,
- #[arg(long, help = "Allowed peer IDs file")]
- pub allowed_peers_path: Option<String>,
- #[arg(long, help = "Don't dial default peers")]
- pub no_default_peers: bool,
- #[arg(long, help = "Bind address", action = ArgAction::Append)]
- pub bind: Vec<String>,
- #[arg(
- long,
- help = "Generate a new peer ID, discarding the existing one",
- default_value = "false"
- )]
- pub new_peer_id: bool,
- #[arg(long, help = "Maximum established incoming connections")]
- pub max_established_incoming: Option<u32>,
- #[arg(long, help = "Maximum established outgoing connections")]
- pub max_established_outgoing: Option<u32>,
- #[arg(long, help = "Maximum pending incoming connections")]
- pub max_pending_incoming: Option<u32>,
- #[arg(long, help = "Maximum pending outgoing connections")]
- pub max_pending_outgoing: Option<u32>,
- #[arg(long, help = "Maximum established connections")]
- pub max_established: Option<u32>,
- #[arg(long, help = "Maximum established connections per peer")]
- pub max_established_per_peer: Option<u32>,
- #[arg(long, help = "Maximum system memory percentage for connection limits")]
- pub max_system_memory_fraction: Option<f64>,
- #[arg(long, help = "Maximum process memory for connection limits (bytes)")]
- pub max_system_memory_bytes: Option<usize>,
- }
- impl NockchainCli {
- pub fn validate(&self) -> Result<(), String> {
- if self.mine && !(self.mining_pubkey.is_some() || self.mining_key_adv.is_some()) {
- return Err(
- "Cannot specify mine without either mining_pubkey or mining_key_adv".to_string(),
- );
- }
- if self.mining_pubkey.is_some() && self.mining_key_adv.is_some() {
- return Err(
- "Cannot specify both mining_pubkey and mining_key_adv at the same time".to_string(),
- );
- }
- if self.genesis_leader && self.genesis_watcher {
- return Err(
- "Cannot specify both genesis_leader and genesis_watcher at the same time"
- .to_string(),
- );
- }
- if !self.fakenet && (self.genesis_watcher || self.genesis_leader) {
- if self.btc_node_url.is_empty() {
- return Err(
- "Must specify --btc-node-url when using genesis_watcher or genesis_leader"
- .to_string(),
- );
- }
- if self.btc_auth_cookie.is_none() {
- if self.btc_username.is_none() && self.btc_password.is_none() {
- return Err("Must specify either --btc-username or --btc-password when using genesis_watcher or genesis_leader on livenet".to_string());
- }
- }
- }
- Ok(())
- }
- /// Helper function to create a BitcoinRPCConnection from CLI arguments
- fn create_bitcoin_connection(&self) -> BitcoinRPCConnection {
- let url = self.btc_node_url.clone();
- let height = GENESIS_HEIGHT;
- let auth = if let Some(username) = self.btc_username.clone() {
- let password = self.btc_password.clone().unwrap_or_else(|| {
- panic!(
- "Panicked at {}:{} (git sha: {:?})",
- file!(),
- line!(),
- option_env!("GIT_SHA")
- )
- });
- bitcoincore_rpc::Auth::UserPass(username, password)
- } else {
- let cookie_path_str = self.btc_auth_cookie.clone().unwrap_or_else(|| {
- panic!(
- "Panicked at {}:{} (git sha: {:?})",
- file!(),
- line!(),
- option_env!("GIT_SHA")
- )
- });
- let cookie_path = PathBuf::from(cookie_path_str);
- bitcoincore_rpc::Auth::CookieFile(cookie_path)
- };
- BitcoinRPCConnection::new(url, auth, height)
- }
- }
- /// # Load a keypair from a file or create a new one if the file doesn't exist
- ///
- /// This function attempts to read a keypair from a specified file. If the file exists, it reads the keypair from the file.
- /// If the file does not exist, it generates a new keypair, writes it to the file, and returns it.
- ///
- /// # Arguments
- /// * `keypair_path` - A reference to a Path object representing the file path where the keypair should be stored
- /// * `force_new` - If true, generate a new keypair even if one already exists
- ///
- /// # Returns
- /// A Result containing the Keypair or an error if any operation fails
- pub fn gen_keypair(keypair_path: &Path) -> Result<Keypair, Box<dyn Error>> {
- let new_keypair = libp2p::identity::Keypair::generate_ed25519();
- let new_keypair_bytes = new_keypair.to_protobuf_encoding()?;
- std::fs::write(keypair_path, new_keypair_bytes)?;
- let peer_id = new_keypair.public().to_peer_id();
- // write the peer_id encoded as base58 to a file
- std::fs::write(
- keypair_path.with_extension(PEER_ID_FILE_EXTENSION),
- peer_id.to_base58(),
- )?;
- info!("Generated new identity as peer {peer_id}");
- Ok(new_keypair)
- }
- fn load_keypair(keypair_path: &Path, force_new: bool) -> Result<Keypair, Box<dyn Error>> {
- if keypair_path.try_exists()? && !force_new {
- let keypair_bytes = std::fs::read(keypair_path)?;
- let keypair = libp2p::identity::Keypair::from_protobuf_encoding(&keypair_bytes[..])?;
- let peer_id = keypair.public().to_peer_id();
- info!("Loaded identity as peer {peer_id}");
- Ok(keypair)
- } else {
- if force_new && keypair_path.try_exists()? {
- info!("Discarding existing peer ID and generating a new one");
- std::fs::remove_file(keypair_path)?;
- }
- gen_keypair(keypair_path)
- }
- }
- #[instrument(skip(kernel_jam, hot_state))]
- pub async fn init_with_kernel(
- cli: Option<NockchainCli>,
- kernel_jam: &[u8],
- hot_state: &[HotEntry],
- ) -> Result<NockApp, Box<dyn Error>> {
- welcome();
- if let Some(cli) = &cli {
- cli.validate()?;
- }
- let mut nockapp = boot::setup(
- kernel_jam,
- cli.as_ref().map(|c| c.nockapp_cli.clone()),
- hot_state,
- "nockchain",
- None,
- )
- .await?;
- let keypair = {
- let keypair_path = Path::new(IDENTITY_PATH);
- load_keypair(
- keypair_path,
- cli.as_ref().map(|c| c.new_peer_id).unwrap_or(false),
- )?
- };
- eprintln!(
- "allowed_peers_path: {:?}",
- cli.as_ref().unwrap().allowed_peers_path
- );
- let allowed = cli.as_ref().and_then(|c| {
- c.allowed_peers_path.as_ref().map(|path| {
- let contents = fs::read_to_string(path).expect("failed to read allowed peers file: {}");
- let peer_ids: Vec<PeerId> = contents
- .lines()
- .map(|line| {
- let peer_id_bytes = bs58::decode(line)
- .into_vec()
- .expect("failed to decode peer ID bytes from base58");
- PeerId::from_bytes(&peer_id_bytes).expect("failed to decode peer ID from bytes")
- })
- .collect();
- let mut allow_behavior =
- allow_block_list::Behaviour::<allow_block_list::AllowedPeers>::default();
- for peer_id in peer_ids {
- allow_behavior.allow_peer(peer_id);
- }
- allow_behavior
- })
- });
- let bind_multiaddrs = cli
- .as_ref()
- .map_or(vec!["/ip4/0.0.0.0/udp/0/quic-v1".parse()?], |c| {
- c.bind
- .clone()
- .into_iter()
- .map(|addr_str| addr_str.parse().expect("could not parse bind multiaddr"))
- .collect()
- });
- let limits = connection_limits::ConnectionLimits::default()
- .with_max_established_incoming(
- cli.as_ref()
- .and_then(|c| c.max_established_incoming)
- .and(Some(MAX_ESTABLISHED_INCOMING_CONNECTIONS)),
- )
- .with_max_established_outgoing(
- cli.as_ref()
- .and_then(|c| c.max_established_outgoing)
- .and(Some(MAX_ESTABLISHED_OUTGOING_CONNECTIONS)),
- )
- .with_max_pending_incoming(
- cli.as_ref()
- .and_then(|c| c.max_pending_incoming)
- .and(Some(MAX_PENDING_INCOMING_CONNECTIONS)),
- )
- .with_max_pending_outgoing(
- cli.as_ref()
- .and_then(|c| c.max_pending_outgoing)
- .and(Some(MAX_PENDING_OUTGOING_CONNECTIONS)),
- )
- .with_max_established(
- cli.as_ref()
- .and_then(|c| c.max_established)
- .and(Some(MAX_ESTABLISHED_CONNECTIONS)),
- )
- .with_max_established_per_peer(
- cli.as_ref()
- .and_then(|c| c.max_established_per_peer)
- .and(Some(MAX_ESTABLISHED_CONNECTIONS_PER_PEER)),
- );
- let memory_limits = cli.as_ref().and_then(|c| {
- if c.max_system_memory_bytes.is_some() && c.max_system_memory_fraction.is_some() { panic!( "Must provide neither or one of --max-system-memory_bytes or --max-system-memory_percentage" )};
- if let Some(max_bytes) = c.max_system_memory_bytes {
- Some(memory_connection_limits::Behaviour::with_max_bytes(max_bytes))
- } else { c.max_system_memory_fraction.map(memory_connection_limits::Behaviour::with_max_percentage) }
- });
- let default_backbone_peers = if cli.as_ref().map(|c| c.fakenet).unwrap_or(false) {
- TESTNET_BACKBONE_NODES
- } else {
- REALNET_BACKBONE_NODES
- };
- let backbone_peers = default_backbone_peers
- .iter()
- .map(|multiaddr_str| {
- multiaddr_str
- .parse()
- .expect("could not parse multiaddr from built-in string")
- })
- .collect();
- // Set up initial peer addresses to connect to
- let mut peer_multiaddrs: Vec<Multiaddr> = if cli.as_ref().is_some_and(|c| c.no_default_peers) {
- Vec::new()
- } else {
- backbone_peers
- };
- if let Some(c) = cli.as_ref() {
- let v: Vec<Multiaddr> = c
- .peer
- .clone()
- .into_iter()
- .map(|multiaddr_str| {
- multiaddr_str
- .parse()
- .expect("could not parse multiaddr from string")
- })
- .collect();
- peer_multiaddrs.extend(v);
- }
- debug!("peer_multiaddrs: {:?}", peer_multiaddrs);
- let equix_builder = equix::EquiXBuilder::new();
- // Create driver initialization signals. the idea here is that we want to wait for
- // drivers that emit init pokes to complete before we send the born poke.
- let mut driver_signals = driver_init::DriverInitSignals::new();
- // Register drivers that need initialization signals
- let mining_init_tx = driver_signals.register_driver("mining");
- let libp2p_init_tx = driver_signals.register_driver("libp2p");
- let watcher_init_tx = driver_signals.register_driver("bitcoin_watcher");
- // Create the born task that waits for all drivers to initialize
- let _born_task = driver_signals.create_born_task();
- if cli.as_ref().map(|c| c.fakenet).unwrap_or(false) {
- let message = cli
- .as_ref()
- .map(|c| c.genesis_message.clone())
- .unwrap_or("".to_string());
- let node_type = if cli.as_ref().map(|c| c.genesis_leader).unwrap_or(false) {
- GenesisNodeType::Leader
- } else {
- GenesisNodeType::Watcher
- };
- let watcher_driver =
- bitcoin_watcher_driver(None, node_type, message, Some(watcher_init_tx));
- nockapp.add_io_driver(watcher_driver).await;
- } else if cli
- .as_ref()
- .map(|c| c.genesis_watcher || c.genesis_leader)
- .unwrap_or(false)
- {
- let message = cli
- .as_ref()
- .map(|c| c.genesis_message.clone())
- .unwrap_or("".to_string());
- let connection = cli.as_ref().unwrap().create_bitcoin_connection();
- let node_type = if cli.as_ref().map(|c| c.genesis_leader).unwrap_or(false) {
- GenesisNodeType::Leader
- } else {
- GenesisNodeType::Watcher
- };
- let watcher_driver =
- bitcoin_watcher_driver(Some(connection), node_type, message, Some(watcher_init_tx));
- nockapp.add_io_driver(watcher_driver).await;
- } else {
- // Realnet with no BTC node
- let mut poke_slab = NounSlab::new();
- let poke_noun = T(
- &mut poke_slab,
- &[D(tas!(b"command")), D(tas!(b"btc-data")), D(0)],
- );
- poke_slab.set_root(poke_noun);
- nockapp
- .poke(nockapp::wire::SystemWire.to_wire(), poke_slab)
- .await
- .expect("Failed to poke for no BTC hash");
- }
- let mining_config = cli.as_ref().and_then(|c| {
- if let Some(pubkey) = &c.mining_pubkey {
- Some(vec![MiningKeyConfig {
- share: 1,
- m: 1,
- keys: vec![pubkey.clone()],
- }])
- } else if let Some(mining_key_adv) = &c.mining_key_adv {
- Some(mining_key_adv.clone())
- } else {
- None
- }
- });
- let mine = cli.as_ref().map_or(false, |c| c.mine);
- let mining_driver =
- crate::mining::create_mining_driver(mining_config, mine, Some(mining_init_tx));
- nockapp.add_io_driver(mining_driver).await;
- let libp2p_driver = nockchain_libp2p_io::nc::make_libp2p_driver(
- keypair,
- bind_multiaddrs,
- allowed,
- limits,
- memory_limits,
- &peer_multiaddrs,
- equix_builder,
- Some(libp2p_init_tx),
- );
- nockapp.add_io_driver(libp2p_driver).await;
- // Create the born driver that waits for the born signal
- let born_driver = driver_signals.create_born_driver();
- // Add the born driver to the nockapp
- nockapp.add_io_driver(born_driver).await;
- // set up socket
- let socket_path = Path::new(
- &cli.as_ref()
- .unwrap_or_else(|| {
- panic!(
- "Panicked at {}:{} (git sha: {:?})",
- file!(),
- line!(),
- option_env!("GIT_SHA")
- )
- })
- .npc_socket,
- );
- nockapp.npc_socket_path = Some(socket_path.to_path_buf());
- if let Some(parent) = socket_path.parent() {
- fs::create_dir_all(parent)?;
- }
- let listener = UnixListener::bind(socket_path)?;
- nockapp
- .add_io_driver(nockapp::npc_listener_driver(listener))
- .await;
- // set up timer
- let mut timer_slab = NounSlab::new();
- let timer_noun = T(
- &mut timer_slab,
- &[D(tas!(b"command")), D(tas!(b"timer")), D(0)],
- );
- timer_slab.set_root(timer_noun);
- nockapp
- .add_io_driver(nockapp::timer_driver(CHAIN_INTERVAL_SECS, timer_slab))
- .await;
- nockapp.add_io_driver(nockapp::exit_driver()).await;
- Ok(nockapp)
- }
- fn welcome() {
- let mut stdout = StandardStream::stdout(ColorChoice::Auto);
- let banner = "
- _ _ _ _ _
- | \\ | | ___ ___| | _____| |__ __ _(_)_ __
- | \\| |/ _ \\ / __| |/ / __| '_ \\ / _` | | '_ \\
- | |\\ | (_) | (__| < (__| | | | (_| | | | | |
- |_| \\_|\\___/ \\___|_|\\_\\___|_| |_|\\__,_|_|_| |_|
- ";
- print_banner(&mut stdout, banner);
- let info = [
- ("Build label", env!("BUILD_EMBED_LABEL")),
- ("Build host", env!("BUILD_HOST")),
- ("Build user", env!("BUILD_USER")),
- ("Build timestamp", env!("BUILD_TIMESTAMP")),
- ("Build date", env!("FORMATTED_DATE")),
- // ("Git commit", env!("BAZEL_GIT_COMMIT")),
- // ("Build timestamp", env!("VERGEN_BUILD_TIMESTAMP")),
- // ("Cargo debug", env!("VERGEN_CARGO_DEBUG")),
- // ("Cargo features", env!("VERGEN_CARGO_FEATURES")),
- // ("Cargo opt level", env!("VERGEN_CARGO_OPT_LEVEL")),
- // ("Cargo target", env!("VERGEN_CARGO_TARGET_TRIPLE")),
- // ("Git branch", env!("VERGEN_GIT_BRANCH")),
- // ("Git commit date", env!("VERGEN_GIT_COMMIT_DATE")),
- // ("Git commit author", env!("VERGEN_GIT_COMMIT_AUTHOR_NAME")),
- // ("Git commit message", env!("VERGEN_GIT_COMMIT_MESSAGE")),
- // ("Git commit timestamp", env!("VERGEN_GIT_COMMIT_TIMESTAMP")),
- // ("Git commit SHA", env!("VERGEN_GIT_SHA")),
- // ("Rustc channel", env!("VERGEN_RUSTC_CHANNEL")),
- // ("Rustc host", env!("VERGEN_RUSTC_HOST_TRIPLE")),
- // ("Rustc LLVM version", env!("VERGEN_RUSTC_LLVM_VERSION")),
- ];
- print_version_info(&mut stdout, &info);
- }
|