use contractless::exit; use contractless::log::{error, logger}; use contractless::startup::daemonize::daemonize_after_wallet_prompt; use contractless::startup::daemonize::handle_control_command; use contractless::startup::initialize_startup::obtain_startup_wallet; use contractless::startup::initialize_startup::prepare_pre_wallet_startup; use contractless::startup::node_runtime::initialize_node_logging; use contractless::startup::node_runtime::install_panic_cleanup; use contractless::startup::node_runtime::run_unlocked_node; use contractless::startup::windows_service::handle_windows_service_command; use contractless::startup::windows_service::try_run_as_windows_service; use contractless::Arc; use contractless::Runtime; use tokio::runtime::Builder; fn main() { // Linux-specific control commands are handled before any startup work begins. match handle_control_command() { Ok(true) => return, Ok(false) => {} Err(e) => { eprintln!("Control command failed: {e}"); exit(1); } } // Windows service management commands are also handled before normal node startup. match handle_windows_service_command() { Ok(true) => return, Ok(false) => {} Err(e) => { eprintln!("Windows service command failed: {e}"); exit(1); } } // If the binary was launched by the Windows Service Control Manager, the service // entrypoint takes over and the normal console path stops here. match try_run_as_windows_service() { Ok(true) => return, Ok(false) => {} Err(e) => { eprintln!("Failed to start Windows service path: {e}"); exit(1); } } // The pre-wallet startup work runs in a temporary runtime so Linux can daemonize // only after the wallet key is obtained, while Windows console launches still use // the same shared startup sequence. let startup_runtime = Builder::new_current_thread() .enable_all() .build() .expect("Failed to create startup runtime"); startup_runtime.block_on(prepare_pre_wallet_startup()); let wallet = startup_runtime.block_on(obtain_startup_wallet()); drop(startup_runtime); // Linux detaches after the wallet prompt unless --foreground is supplied. if let Err(e) = daemonize_after_wallet_prompt() { eprintln!("Failed to daemonize: {e}"); exit(1); } // Once the platform-specific startup path is settled, the shared node runtime // takes over for normal unlocked operation. let runtime = Runtime::new().expect("Failed to create main runtime"); let _log_handle = runtime.block_on(async { match initialize_node_logging().await { Ok(handle) => Some(handle), Err(e) => { eprintln!("Failed to initialize logging: {e}"); None } } }); install_panic_cleanup(); if let Err(e) = runtime.block_on(run_unlocked_node(Arc::new(wallet), true)) { error!("Failed to start unlocked node runtime: {e}"); logger().flush(); eprintln!("Failed to start unlocked node runtime: {e}"); exit(1); } }