diff --git a/gui/src/bitcoind.rs b/gui/src/bitcoind.rs index 275b5452..57f597a3 100644 --- a/gui/src/bitcoind.rs +++ b/gui/src/bitcoind.rs @@ -1,6 +1,7 @@ use liana::{config::BitcoindConfig, miniscript::bitcoin}; use std::path::Path; use std::sync::Arc; +use tokio::sync::Mutex; use tracing::{info, warn}; @@ -51,6 +52,7 @@ impl std::fmt::Display for StartInternalBitcoindError { pub struct Bitcoind { _process: Arc, pub config: BitcoindConfig, + pub stdout: Option>>, } impl Bitcoind { @@ -85,9 +87,9 @@ impl Bitcoind { #[cfg(target_os = "windows")] let command = command.creation_flags(CREATE_NO_WINDOW); - let process = command + let mut process = command .args(&args) - .stdout(std::process::Stdio::null()) // We still get bitcoind's logs in debug.log. + .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()))?; @@ -114,6 +116,7 @@ impl Bitcoind { .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), }) diff --git a/gui/src/loader.rs b/gui/src/loader.rs index 6c6e45f0..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, @@ -46,6 +49,7 @@ pub enum Step { Syncing { daemon: Arc, progress: f64, + bitcoind_logs: String, }, Error(Box), } @@ -68,6 +72,7 @@ pub enum Message { ), Started(Result<(Arc, Option), Error>), Loaded(Result, Error>), + BitcoindLog(Option), Failure(DaemonError), } @@ -102,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."); @@ -137,16 +143,30 @@ impl Loader { Command::none() } + 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, bitcoind)) => { - self.internal_bitcoind = 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) } @@ -221,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() @@ -234,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 { @@ -296,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)),