diff --git a/gui/src/app/mod.rs b/gui/src/app/mod.rs index 42f1aec8..25c9c86d 100644 --- a/gui/src/app/mod.rs +++ b/gui/src/app/mod.rs @@ -31,7 +31,7 @@ use state::{ use crate::{ app::{cache::Cache, error::Error, menu::Menu, wallet::Wallet}, - bitcoind::stop_internal_bitcoind, + bitcoind::Bitcoind, daemon::{embedded::EmbeddedDaemon, Daemon}, }; @@ -42,6 +42,7 @@ pub struct App { config: Config, wallet: Arc, daemon: Arc, + internal_bitcoind: Option, } impl App { @@ -51,6 +52,7 @@ impl App { config: Config, daemon: Arc, data_dir: PathBuf, + internal_bitcoind: Option, ) -> (App, Command) { let state: Box = Home::new(wallet.clone(), &cache.coins).into(); let cmd = state.load(daemon.clone()); @@ -62,6 +64,7 @@ impl App { config, daemon, wallet, + internal_bitcoind, }, cmd, ) @@ -69,9 +72,12 @@ impl App { fn load_state(&mut self, menu: &Menu) -> Command { self.state = match menu { - menu::Menu::Settings => { - state::SettingsState::new(self.data_dir.clone(), self.wallet.clone()).into() - } + menu::Menu::Settings => state::SettingsState::new( + self.data_dir.clone(), + self.wallet.clone(), + self.internal_bitcoind.is_some(), + ) + .into(), menu::Menu::Home => Home::new(self.wallet.clone(), &self.cache.coins).into(), menu::Menu::Coins => CoinsPanel::new( &self.cache.coins, @@ -116,12 +122,8 @@ impl App { if !self.daemon.is_external() { self.daemon.stop(); info!("Internal daemon stopped"); - if self.config.internal_bitcoind_exe_config.is_some() { - if let Some(daemon_config) = self.daemon.config() { - if let Some(bitcoind_config) = &daemon_config.bitcoind_config { - stop_internal_bitcoind(bitcoind_config); - } - } + if let Some(bitcoind) = &self.internal_bitcoind { + bitcoind.stop(); } } } diff --git a/gui/src/app/state/settings/bitcoind.rs b/gui/src/app/state/settings/bitcoind.rs index b5c098cf..6b3b529a 100644 --- a/gui/src/app/state/settings/bitcoind.rs +++ b/gui/src/app/state/settings/bitcoind.rs @@ -22,13 +22,19 @@ pub struct BitcoindSettingsState { warning: Option, config_updated: bool, daemon_is_external: bool, + bitcoind_is_internal: bool, settings: Vec>, current: Option, } impl BitcoindSettingsState { - pub fn new(config: Option, cache: &Cache, daemon_is_external: bool) -> Self { + pub fn new( + config: Option, + cache: &Cache, + daemon_is_external: bool, + bitcoind_is_internal: bool, + ) -> Self { let settings = if let Some(config) = &config { vec![ BitcoindSettings::new( @@ -44,6 +50,7 @@ impl BitcoindSettingsState { BitcoindSettingsState { daemon_is_external, + bitcoind_is_internal, warning: None, config_updated: false, settings, @@ -106,7 +113,8 @@ impl State for BitcoindSettingsState { } fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> { - let can_edit = self.current.is_none() && !self.daemon_is_external; + let can_edit = + self.current.is_none() && !self.daemon_is_external && !self.bitcoind_is_internal; view::settings::bitcoind_settings( cache, self.warning.as_ref(), diff --git a/gui/src/app/state/settings/mod.rs b/gui/src/app/state/settings/mod.rs index 471a7e00..fdce1ca3 100644 --- a/gui/src/app/state/settings/mod.rs +++ b/gui/src/app/state/settings/mod.rs @@ -32,14 +32,16 @@ pub struct SettingsState { data_dir: PathBuf, wallet: Arc, setting: Option>, + internal_bitcoind: bool, } impl SettingsState { - pub fn new(data_dir: PathBuf, wallet: Arc) -> Self { + pub fn new(data_dir: PathBuf, wallet: Arc, internal_bitcoind: bool) -> Self { Self { data_dir, wallet, setting: None, + internal_bitcoind, } } } @@ -58,6 +60,7 @@ impl State for SettingsState { daemon.config().cloned(), cache, daemon.is_external(), + self.internal_bitcoind, ) .into(), ); diff --git a/gui/src/bitcoind.rs b/gui/src/bitcoind.rs index aba7cfb8..57f597a3 100644 --- a/gui/src/bitcoind.rs +++ b/gui/src/bitcoind.rs @@ -1,9 +1,10 @@ use liana::{config::BitcoindConfig, miniscript::bitcoin}; +use std::path::Path; +use std::sync::Arc; +use tokio::sync::Mutex; use tracing::{info, warn}; -use crate::app::config::InternalBitcoindExeConfig; - #[cfg(target_os = "windows")] use std::os::windows::process::CommandExt; @@ -47,51 +48,91 @@ impl std::fmt::Display for StartInternalBitcoindError { } } } - -/// Start internal bitcoind for the given network. -pub fn start_internal_bitcoind( - network: &bitcoin::Network, - exe_config: InternalBitcoindExeConfig, -) -> Result { - let datadir_path_str = exe_config - .data_dir - .canonicalize() - .map_err(|e| StartInternalBitcoindError::CouldNotCanonicalizeDataDir(e.to_string()))? - .to_str() - .ok_or_else(|| { - StartInternalBitcoindError::CouldNotCanonicalizeDataDir( - "Couldn't convert path to str.".to_string(), - ) - })? - .to_string(); - #[cfg(target_os = "windows")] - // See https://github.com/rust-lang/rust/issues/42869. - let datadir_path_str = datadir_path_str.replace("\\\\?\\", "").replace("\\\\?", ""); - let args = vec![ - format!("-chain={}", network.to_core_arg()), - format!("-datadir={}", datadir_path_str), - ]; - let mut command = std::process::Command::new(exe_config.exe_path); - #[cfg(target_os = "windows")] - let command = command.creation_flags(CREATE_NO_WINDOW); - command - .args(&args) - .stdout(std::process::Stdio::null()) // We still get bitcoind's logs in debug.log. - .stderr(std::process::Stdio::piped()) - .spawn() - .map_err(|e| StartInternalBitcoindError::CommandError(e.to_string())) +#[derive(Debug, Clone)] +pub struct Bitcoind { + _process: Arc, + pub config: BitcoindConfig, + pub stdout: Option>>, } -/// Stop (internal) bitcoind. -pub fn stop_internal_bitcoind(bitcoind_config: &BitcoindConfig) { - match liana::BitcoinD::new(bitcoind_config, "internal_bitcoind_stop".to_string()) { - Ok(bitcoind) => { - info!("Stopping internal bitcoind..."); - bitcoind.stop(); - info!("Stopped liana managed bitcoind"); +impl Bitcoind { + /// Start internal bitcoind for the given network. + pub fn start( + network: &bitcoin::Network, + mut config: BitcoindConfig, + bitcoind_datadir: &Path, + exe_path: &Path, + ) -> Result { + let datadir_path_str = bitcoind_datadir + .canonicalize() + .map_err(|e| StartInternalBitcoindError::CouldNotCanonicalizeDataDir(e.to_string()))? + .to_str() + .ok_or_else(|| { + StartInternalBitcoindError::CouldNotCanonicalizeDataDir( + "Couldn't convert path to str.".to_string(), + ) + })? + .to_string(); + + // See https://github.com/rust-lang/rust/issues/42869. + #[cfg(target_os = "windows")] + let datadir_path_str = datadir_path_str.replace("\\\\?\\", "").replace("\\\\?", ""); + + let args = vec![ + format!("-chain={}", network.to_core_arg()), + format!("-datadir={}", datadir_path_str), + ]; + let mut command = std::process::Command::new(exe_path); + + #[cfg(target_os = "windows")] + let command = command.creation_flags(CREATE_NO_WINDOW); + + let mut process = command + .args(&args) + .stdout(std::process::Stdio::piped()) // We still get bitcoind's logs in debug.log. + .stderr(std::process::Stdio::piped()) + .spawn() + .map_err(|e| StartInternalBitcoindError::CommandError(e.to_string()))?; + + if !crate::utils::poll_for_file(&config.cookie_path, 200, 15) { + match process.wait_with_output() { + Err(e) => { + tracing::error!("Error while waiting for bitcoind to finish: {}", e) + } + Ok(o) => { + tracing::error!("Exit status: {}", o.status); + tracing::error!("stderr: {}", String::from_utf8_lossy(&o.stderr)); + } + } + return Err(StartInternalBitcoindError::CookieFileNotFound( + config.cookie_path.to_string_lossy().into_owned(), + )); } - Err(e) => { - warn!("Could not create interface to internal bitcoind: '{}'.", e); + config.cookie_path = config.cookie_path.canonicalize().map_err(|e| { + StartInternalBitcoindError::CouldNotCanonicalizeCookiePath(e.to_string()) + })?; + + liana::BitcoinD::new(&config, "internal_bitcoind_start".to_string()) + .map_err(|e| StartInternalBitcoindError::BitcoinDError(e.to_string()))?; + + Ok(Self { + stdout: process.stdout.take().map(|s| Arc::new(Mutex::new(s))), + config, + _process: Arc::new(process), + }) + } + + /// Stop (internal) bitcoind. + pub fn stop(&self) { + match liana::BitcoinD::new(&self.config, "internal_bitcoind_stop".to_string()) { + Ok(bitcoind) => { + info!("Stopping internal bitcoind..."); + bitcoind.stop(); + info!("Stopped liana managed bitcoind"); + } + Err(e) => { + warn!("Could not create interface to internal bitcoind: '{}'.", e); + } } } } diff --git a/gui/src/installer/context.rs b/gui/src/installer/context.rs index 7d1a6bfb..6bca248b 100644 --- a/gui/src/installer/context.rs +++ b/gui/src/installer/context.rs @@ -8,6 +8,7 @@ use crate::{ settings::{KeySetting, Settings, WalletSetting}, wallet::DEFAULT_WALLET_NAME, }, + bitcoind::Bitcoind, hw::HardwareWalletConfig, signer::Signer, }; @@ -36,6 +37,7 @@ pub struct Context { pub bitcoind_is_external: bool, pub internal_bitcoind_config: Option, pub internal_bitcoind_exe_config: Option, + pub internal_bitcoind: Option, } impl Context { @@ -55,6 +57,7 @@ impl Context { bitcoind_is_external: true, internal_bitcoind_config: None, internal_bitcoind_exe_config: None, + internal_bitcoind: None, } } diff --git a/gui/src/installer/message.rs b/gui/src/installer/message.rs index 9aaf9477..05b2dd06 100644 --- a/gui/src/installer/message.rs +++ b/gui/src/installer/message.rs @@ -5,7 +5,7 @@ use liana::miniscript::{ use std::path::PathBuf; use super::Error; -use crate::{download::Progress, hw::HardwareWallet}; +use crate::{bitcoind::Bitcoind, download::Progress, hw::HardwareWallet}; use async_hwi::DeviceKind; #[derive(Debug, Clone)] @@ -14,7 +14,7 @@ pub enum Message { ParticipateWallet, ImportWallet, UserActionDone(bool), - Exit(PathBuf), + Exit(PathBuf, Option), Clibpboard(String), Next, Skip, diff --git a/gui/src/installer/mod.rs b/gui/src/installer/mod.rs index c8ada36d..b581c075 100644 --- a/gui/src/installer/mod.rs +++ b/gui/src/installer/mod.rs @@ -18,7 +18,6 @@ use std::sync::{Arc, Mutex}; use crate::{ app::config::InternalBitcoindExeConfig, app::{config as gui_config, settings as gui_settings}, - bitcoind::stop_internal_bitcoind, signer::Signer, }; @@ -84,11 +83,10 @@ impl Installer { .expect("There is always a step") .stop(); // Now use context to determine what to stop. - if self.context.internal_bitcoind_config.is_some() { - if let Some(bitcoind_config) = &self.context.bitcoind_config { - stop_internal_bitcoind(bitcoind_config); - } + if let Some(bitcoind) = &self.context.internal_bitcoind { + bitcoind.stop(); } + self.context.internal_bitcoind = None; } fn next(&mut self) -> Command { diff --git a/gui/src/installer/step/bitcoind.rs b/gui/src/installer/step/bitcoind.rs index 1320be7e..89a3e95b 100644 --- a/gui/src/installer/step/bitcoind.rs +++ b/gui/src/installer/step/bitcoind.rs @@ -12,14 +12,14 @@ use iced::{Command, Subscription}; use liana::{config::BitcoindConfig, miniscript::bitcoin::Network}; #[cfg(any(target_os = "macos", target_os = "linux"))] use tar::Archive; -use tracing::{error, info}; +use tracing::info; use jsonrpc::{client::Client, simple_http::SimpleHttpTransport}; use liana_ui::{component::form, widget::*}; use crate::{ - bitcoind::{start_internal_bitcoind, stop_internal_bitcoind, StartInternalBitcoindError}, + bitcoind::{Bitcoind, StartInternalBitcoindError}, download, installer::{ context::Context, @@ -28,7 +28,6 @@ use crate::{ step::Step, view, Error, InternalBitcoindExeConfig, }, - utils::poll_for_file, }; // The approach for tracking download progress is taken from @@ -133,30 +132,6 @@ fn download_url() -> String { ) } -pub struct DefineBitcoind { - cookie_path: form::Value, - address: form::Value, - is_running: Option>, -} - -pub struct InternalBitcoindStep { - liana_datadir: PathBuf, - bitcoind_datadir: PathBuf, - network: Network, - started: Option>, - exe_path: Option, - bitcoind_config: Option, - exe_config: Option, - internal_bitcoind_config: Option, - error: Option, - exe_download: Option, - install_state: Option, -} - -pub struct SelectBitcoindTypeStep { - use_external: bool, -} - /// Default prune value used by internal bitcoind. pub const PRUNE_DEFAULT: u32 = 15_000; /// Default ports used by bitcoind across all networks. @@ -511,6 +486,10 @@ pub fn port_is_valid(port: &u16) -> bool { !BITCOIND_DEFAULT_PORTS.contains(port) } +pub struct SelectBitcoindTypeStep { + use_external: bool, +} + impl Default for SelectBitcoindTypeStep { fn default() -> Self { Self::new() @@ -560,6 +539,12 @@ impl Step for SelectBitcoindTypeStep { } } +pub struct DefineBitcoind { + cookie_path: form::Value, + address: form::Value, + is_running: Option>, +} + impl DefineBitcoind { pub fn new() -> Self { Self { @@ -682,6 +667,21 @@ impl From for Box { } } +pub struct InternalBitcoindStep { + liana_datadir: PathBuf, + bitcoind_datadir: PathBuf, + network: Network, + started: Option>, + exe_path: Option, + bitcoind_config: Option, + exe_config: Option, + internal_bitcoind_config: Option, + error: Option, + exe_download: Option, + install_state: Option, + internal_bitcoind: Option, +} + impl From for Box { fn from(s: InternalBitcoindStep) -> Box { Box::new(s) @@ -702,6 +702,7 @@ impl InternalBitcoindStep { error: None, exe_download: None, install_state: None, + internal_bitcoind: None, } } } @@ -727,11 +728,9 @@ impl Step for InternalBitcoindStep { if let Message::InternalBitcoind(msg) = message { match msg { message::InternalBitcoindMsg::Previous => { - if self.internal_bitcoind_config.is_some() { - if let Some(bitcoind_config) = &self.bitcoind_config { - stop_internal_bitcoind(bitcoind_config); - self.started = None; - } + if let Some(bitcoind) = &self.internal_bitcoind { + bitcoind.stop(); + self.started = None; } return Command::perform(async {}, |_| Message::Previous); } @@ -865,36 +864,9 @@ impl Step for InternalBitcoindStep { return Command::none(); } }; - let handle = - match start_internal_bitcoind(&self.network, exe_config.clone()) { - Err(e) => { - self.started = Some(Err( - StartInternalBitcoindError::CommandError(e.to_string()), - )); - return Command::none(); - } - Ok(h) => h, - }; - // Need to wait for cookie file to appear. let cookie_path = internal_bitcoind_cookie_path(&self.bitcoind_datadir, &self.network); - if !poll_for_file(&cookie_path, 200, 15) { - error!("Cookie file still not present after 3 seconds. Waiting for the bitcoind process to finish."); - match handle.wait_with_output() { - Err(e) => { - error!("Error while waiting for bitcoind to finish: {}", e) - } - Ok(o) => { - error!("Exit status: {}", o.status); - error!("stderr: {}", String::from_utf8_lossy(&o.stderr)); - } - } - self.started = - Some(Err(StartInternalBitcoindError::CookieFileNotFound( - cookie_path.to_string_lossy().into_owned(), - ))); - return Command::none(); - } + let rpc_port = self .internal_bitcoind_config .as_ref() @@ -904,36 +876,30 @@ impl Step for InternalBitcoindStep { .get(&self.network) .expect("Already added") .rpc_port; - let bitcoind_config = match cookie_path.canonicalize() { - Ok(cookie_path) => BitcoindConfig { + + match Bitcoind::start( + &self.network, + BitcoindConfig { cookie_path, addr: internal_bitcoind_address(rpc_port), }, + &exe_config.data_dir, + &exe_config.exe_path, + ) { Err(e) => { - self.started = Some(Err( - StartInternalBitcoindError::CouldNotCanonicalizeCookiePath( - e.to_string(), - ), - )); + self.started = Some(Err(StartInternalBitcoindError::CommandError( + e.to_string(), + ))); return Command::none(); } - }; - match liana::BitcoinD::new( - &bitcoind_config, - "internal_bitcoind_connection_check".to_string(), - ) { - Ok(_) => { + Ok(bitcoind) => { self.error = None; - self.bitcoind_config = Some(bitcoind_config); + self.bitcoind_config = Some(bitcoind.config.clone()); self.exe_config = Some(exe_config); self.started = Some(Ok(())); + self.internal_bitcoind = Some(bitcoind); } - Err(e) => { - self.started = Some(Err( - StartInternalBitcoindError::BitcoinDError(e.to_string()), - )); - } - } + }; } } }; @@ -975,6 +941,7 @@ impl Step for InternalBitcoindStep { ctx.bitcoind_config = self.bitcoind_config.clone(); ctx.internal_bitcoind_config = self.internal_bitcoind_config.clone(); ctx.internal_bitcoind_exe_config = self.exe_config.clone(); + ctx.internal_bitcoind = self.internal_bitcoind.clone(); self.error = None; return true; } @@ -994,10 +961,8 @@ impl Step for InternalBitcoindStep { fn stop(&self) { // In case the installer is closed before changes written to context, stop bitcoind. - if let Some(Ok(_)) = self.started { - if let Some(bitcoind_config) = &self.bitcoind_config { - stop_internal_bitcoind(bitcoind_config); - } + if let Some(bitcoind) = &self.internal_bitcoind { + bitcoind.stop(); } } diff --git a/gui/src/installer/view.rs b/gui/src/installer/view.rs index ce7a0e4a..e4947e1b 100644 --- a/gui/src/installer/view.rs +++ b/gui/src/installer/view.rs @@ -1248,7 +1248,10 @@ pub fn install<'a>( .push(Container::new(text("Installed !"))) .push(Container::new( button::primary(None, "Start") - .on_press(Message::Exit(path.clone())) + .on_press(Message::Exit( + path.clone(), + context.internal_bitcoind.clone(), + )) .width(Length::Fixed(200.0)), )) .align_items(Alignment::Center) diff --git a/gui/src/loader.rs b/gui/src/loader.rs index 351274b9..e6523935 100644 --- a/gui/src/loader.rs +++ b/gui/src/loader.rs @@ -1,5 +1,7 @@ use std::convert::From; +use std::io::BufRead; use std::io::ErrorKind; +use std::ops::DerefMut; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -12,6 +14,7 @@ use liana::{ StartupError, }; use liana_ui::{ + color, component::{button, notification, text::*}, icon, util::Collection, @@ -24,9 +27,8 @@ use crate::{ config::{Config as GUIConfig, InternalBitcoindExeConfig}, wallet::{Wallet, WalletError}, }, - bitcoind::{start_internal_bitcoind, stop_internal_bitcoind, StartInternalBitcoindError}, + bitcoind::{Bitcoind, StartInternalBitcoindError}, daemon::{client, embedded::EmbeddedDaemon, model::*, Daemon, DaemonError}, - utils, }; type Lianad = client::Lianad; @@ -36,6 +38,7 @@ pub struct Loader { pub network: bitcoin::Network, pub gui_config: GUIConfig, pub daemon_started: bool, + pub internal_bitcoind: Option, step: Step, } @@ -46,6 +49,7 @@ pub enum Step { Syncing { daemon: Arc, progress: f64, + bitcoind_logs: String, }, Error(Box), } @@ -55,9 +59,20 @@ pub enum Step { pub enum Message { View(ViewMessage), Syncing(Result), - Synced(Result<(Arc, Cache, Arc), Error>), - Started(Result, Error>), + Synced( + Result< + ( + Arc, + Cache, + Arc, + Option, + ), + Error, + >, + ), + Started(Result<(Arc, Option), Error>), Loaded(Result, Error>), + BitcoindLog(Option), Failure(DaemonError), } @@ -66,6 +81,7 @@ impl Loader { datadir_path: PathBuf, gui_config: GUIConfig, network: bitcoin::Network, + internal_bitcoind: Option, ) -> (Self, Command) { let path = gui_config .daemon_rpc_path @@ -79,6 +95,7 @@ impl Loader { gui_config, step: Step::Connecting, daemon_started: false, + internal_bitcoind, }, Command::perform(connect(path), Message::Loaded), ) @@ -90,6 +107,7 @@ impl Loader { self.step = Step::Syncing { daemon: daemon.clone(), progress: 0.0, + bitcoind_logs: String::new(), }; if self.gui_config.internal_bitcoind_exe_config.is_some() { warn!("Ignoring internal bitcoind config because Liana daemon is external."); @@ -125,12 +143,30 @@ impl Loader { Command::none() } - fn on_start(&mut self, res: Result, Error>) -> Command { + fn on_log(&mut self, log: Option) -> Command { + if let Step::Syncing { bitcoind_logs, .. } = &mut self.step { + if let Some(l) = log { + *bitcoind_logs = l; + } + } + Command::none() + } + + fn on_start( + &mut self, + res: Result<(Arc, Option), Error>, + ) -> Command { match res { - Ok(daemon) => { + Ok((daemon, bitcoind)) => { + // bitcoind may have been already started and given to the loader + // We should not override with None the loader bitcoind field + if let Some(bitcoind) = bitcoind { + self.internal_bitcoind = Some(bitcoind); + } self.step = Step::Syncing { daemon: daemon.clone(), progress: 0.0, + bitcoind_logs: String::new(), }; Command::perform(sync(daemon, false), Message::Syncing) } @@ -156,6 +192,7 @@ impl Loader { self.gui_config.clone(), self.datadir_path.clone(), self.network, + self.internal_bitcoind.take(), ), Message::Synced, ); @@ -183,18 +220,9 @@ impl Loader { info!("Internal daemon stopped"); } } - if self.gui_config.internal_bitcoind_exe_config.is_some() { - if let Ok(daemon_config) = - Config::from_file(self.gui_config.daemon_config_path.as_ref().cloned()) - { - if let Some(bitcoind_config) = &daemon_config.bitcoind_config { - stop_internal_bitcoind(bitcoind_config); - } else { - warn!("Liana daemon config does not have bitcoind config"); - } - } else { - warn!("Liana gui cannot access daemon config to stop bitcoind"); - } + + if let Some(bitcoind) = &self.internal_bitcoind { + bitcoind.stop(); } } @@ -205,6 +233,7 @@ impl Loader { self.datadir_path.clone(), self.gui_config.clone(), self.network, + self.internal_bitcoind.clone(), ); *self = loader; cmd @@ -212,6 +241,7 @@ impl Loader { Message::Started(res) => self.on_start(res), Message::Loaded(res) => self.on_load(res), Message::Syncing(res) => self.on_sync(res), + Message::BitcoindLog(log) => self.on_log(log), Message::Synced(Err(e)) => { self.step = Step::Error(Box::new(e)); Command::none() @@ -225,7 +255,24 @@ impl Loader { } pub fn subscription(&self) -> Subscription { - Subscription::none() + if let Some(Some(bitcoind_stdout)) = + self.internal_bitcoind.as_ref().map(|b| b.stdout.clone()) + { + iced::subscription::unfold(0, bitcoind_stdout, move |stdout| async { + let msg = { + let mut s = stdout.lock().await; + let mut s = std::io::BufReader::new(s.deref_mut()); + let mut buffer = String::new(); + match s.read_line(&mut buffer) { + Err(e) => Message::BitcoindLog(Some(e.to_string())), + Ok(_) => Message::BitcoindLog(Some(buffer)), + } + }; + (msg, stdout) + }) + } else { + Subscription::none() + } } pub fn view(&self) -> Element { @@ -239,7 +286,16 @@ pub async fn load_application( gui_config: GUIConfig, datadir_path: PathBuf, network: bitcoin::Network, -) -> Result<(Arc, Cache, Arc), Error> { + internal_bitcoind: Option, +) -> Result< + ( + Arc, + Cache, + Arc, + Option, + ), + Error, +> { let coins = daemon.list_coins().map(|res| res.coins)?; let spend_txs = daemon.list_spend_transactions()?; let cache = Cache { @@ -253,7 +309,7 @@ pub async fn load_application( let wallet = Wallet::new(info.descriptors.main).load_settings(&gui_config, &datadir_path, network)?; - Ok((Arc::new(wallet), cache, daemon)) + Ok((Arc::new(wallet), cache, daemon, internal_bitcoind)) } #[derive(Clone, Debug)] @@ -278,12 +334,17 @@ pub fn view(step: &Step) -> Element { .push(ProgressBar::new(0.0..=1.0, 0.0).width(Length::Fill)) .push(text("Connecting to daemon...")), ), - Step::Syncing { progress, .. } => cover( + Step::Syncing { + progress, + bitcoind_logs, + .. + } => cover( None, Column::new() .width(Length::Fill) .push(ProgressBar::new(0.0..=1.0, *progress as f32).width(Length::Fill)) - .push(text("Syncing the wallet with the blockchain...")), + .push(text("Syncing the wallet with the blockchain...")) + .push(p2_regular(bitcoind_logs).style(color::GREY_3)), ), Step::Error(error) => cover( Some(("Error while starting the internal daemon", error)), @@ -351,8 +412,9 @@ async fn connect(socket_path: PathBuf) -> Result, pub async fn start_bitcoind_and_daemon( config_path: PathBuf, bitcoind_exe_config: Option, -) -> Result, Error> { +) -> Result<(Arc, Option), Error> { let config = Config::from_file(Some(config_path)).map_err(Error::Config)?; + let mut bitcoind: Option = None; if let Some(exe_config) = bitcoind_exe_config { if let Some(bitcoind_config) = &config.bitcoind_config { // Check if bitcoind is already running before trying to start it. @@ -361,19 +423,15 @@ pub async fn start_bitcoind_and_daemon( info!("Internal bitcoind is already running"); } else { info!("Starting internal bitcoind"); - start_internal_bitcoind(&config.bitcoin_config.network, exe_config) - .map_err(Error::Bitcoind)?; - if !utils::poll_for_file(&bitcoind_config.cookie_path, 200, 15) { - return Err(Error::Bitcoind( - StartInternalBitcoindError::CookieFileNotFound( - bitcoind_config.cookie_path.to_string_lossy().into_owned(), - ), - )); - } - liana::BitcoinD::new(bitcoind_config, "internal_bitcoind_start".to_string()) - .map_err(|e| { - Error::Bitcoind(StartInternalBitcoindError::BitcoinDError(e.to_string())) - })?; + bitcoind = Some( + Bitcoind::start( + &config.bitcoin_config.network, + bitcoind_config.clone(), + &exe_config.data_dir, + &exe_config.exe_path, + ) + .map_err(Error::Bitcoind)?, + ); } } } @@ -382,7 +440,7 @@ pub async fn start_bitcoind_and_daemon( let daemon = EmbeddedDaemon::start(config)?; - Ok(Arc::new(daemon)) + Ok((Arc::new(daemon), bitcoind)) } async fn sync( diff --git a/gui/src/main.rs b/gui/src/main.rs index 479d6a9a..c70f669d 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -142,7 +142,7 @@ impl Application for GUI { network, cfg.log_level().unwrap_or(LevelFilter::INFO), ); - let (loader, command) = Loader::new(datadir_path, cfg, network); + let (loader, command) = Loader::new(datadir_path, cfg, network, None); ( Self { state: State::Loader(Box::new(loader)), @@ -189,14 +189,14 @@ impl Application for GUI { network, cfg.log_level().unwrap_or(LevelFilter::INFO), ); - let (loader, command) = Loader::new(datadir_path, cfg, network); + let (loader, command) = Loader::new(datadir_path, cfg, network, None); self.state = State::Loader(Box::new(loader)); command.map(|msg| Message::Load(Box::new(msg))) } _ => l.update(*msg).map(|msg| Message::Launch(Box::new(msg))), }, (State::Installer(i), Message::Install(msg)) => { - if let installer::Message::Exit(path) = *msg { + if let installer::Message::Exit(path, internal_bitcoind) = *msg { let cfg = app::Config::from_file(&path).unwrap(); let daemon_cfg = DaemonConfig::from_file(cfg.daemon_config_path.clone()).unwrap(); @@ -212,8 +212,12 @@ impl Application for GUI { cfg.log_level().unwrap_or(LevelFilter::INFO), ); self.logger.remove_install_log_file(datadir_path.clone()); - let (loader, command) = - Loader::new(datadir_path, cfg, daemon_cfg.bitcoin_config.network); + let (loader, command) = Loader::new( + datadir_path, + cfg, + daemon_cfg.bitcoin_config.network, + internal_bitcoind, + ); self.state = State::Loader(Box::new(loader)); command.map(|msg| Message::Load(Box::new(msg))) } else { @@ -226,13 +230,14 @@ impl Application for GUI { State::Launcher(Box::new(Launcher::new(loader.datadir_path.clone()))); Command::none() } - loader::Message::Synced(Ok((wallet, cache, daemon))) => { + loader::Message::Synced(Ok((wallet, cache, daemon, bitcoind))) => { let (app, command) = App::new( cache, wallet, loader.gui_config.clone(), daemon, loader.datadir_path.clone(), + bitcoind, ); self.state = State::App(app); command.map(|msg| Message::Run(Box::new(msg)))