use std::{ fs, path::PathBuf, process::exit, sync::{Arc, Mutex}, }; use clap::Parser; use common::utils::hex_str_to_bytes; use dirs::home_dir; use env_logger::Env; use eyre::Result; use client::{database::FileDB, Client}; use config::{networks, Config}; use futures::executor::block_on; use log::info; #[tokio::main] async fn main() -> Result<()> { env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); let config = get_config(); let mut client = Client::new(config).await?; client.start().await?; register_shutdown_handler(client); std::future::pending().await } fn register_shutdown_handler(client: Client) { let client = Arc::new(client); let shutdown_counter = Arc::new(Mutex::new(0)); ctrlc::set_handler(move || { let mut counter = shutdown_counter.lock().unwrap(); *counter += 1; let counter_value = *counter; if counter_value == 3 { info!("forced shutdown"); exit(0); } info!( "shutting down... press ctrl-c {} more times to force quit", 3 - counter_value ); if counter_value == 1 { let client = client.clone(); std::thread::spawn(move || { block_on(client.shutdown()); exit(0); }); } }) .expect("could not register shutdown handler"); } fn get_config() -> Config { let cli = Cli::parse(); let mut config = match cli.network.as_str() { "mainnet" => networks::mainnet(), "goerli" => networks::goerli(), _ => { let home = home_dir().unwrap(); let config_path = home.join(format!(".lightclient/configs/{}.toml", cli.network)); Config::from_file(&config_path).expect("could not read network config") } }; let data_dir = get_data_dir(&cli); config.general.checkpoint = match cli.checkpoint { Some(checkpoint) => hex_str_to_bytes(&checkpoint).expect("invalid checkpoint"), None => get_cached_checkpoint(&data_dir).unwrap_or(config.general.checkpoint), }; config.general.execution_rpc = Some(cli.execution_rpc); if let Some(port) = cli.port { config.general.rpc_port = Some(port); } if let Some(consensus_rpc) = cli.consensus_rpc { config.general.consensus_rpc = consensus_rpc; } config.machine.data_dir = Some(data_dir); config } fn get_data_dir(cli: &Cli) -> PathBuf { match &cli.data_dir { Some(dir) => PathBuf::from(dir), None => home_dir() .unwrap() .join(format!(".lightclient/data/{}", cli.network)), } } fn get_cached_checkpoint(data_dir: &PathBuf) -> Option> { let checkpoint_file = data_dir.join("checkpoint"); if checkpoint_file.exists() { let checkpoint_res = fs::read(checkpoint_file); match checkpoint_res { Ok(checkpoint) => Some(checkpoint), Err(_) => None, } } else { None } } #[derive(Parser)] struct Cli { #[clap(short, long, default_value = "mainnet")] network: String, #[clap(short, long)] port: Option, #[clap(short = 'w', long)] checkpoint: Option, #[clap(short, long)] execution_rpc: String, #[clap(short, long)] consensus_rpc: Option, #[clap(long)] data_dir: Option, }