Skip to content

Commit

Permalink
gui: add option to use internal bitcoind
Browse files Browse the repository at this point in the history
During installation, the user may choose for Liana to configure and start bitcoind for them.

This internal bitcoind uses as its data directory the `bitcoind_datadir` folder within the
Liana data directory.

If the internal bitcoind option has been selected for a network, it will be automatically
started when the user returns to Liana and stopped when Liana is closed.
  • Loading branch information
jp1ac4 committed Aug 23, 2023
1 parent 765c68b commit 36cf85d
Show file tree
Hide file tree
Showing 14 changed files with 1,049 additions and 31 deletions.
85 changes: 85 additions & 0 deletions gui/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ toml = "0.5"

chrono = "0.4"

rust-ini = "0.19.0"
which = "4.4.0"

[dev-dependencies]
tokio = {version = "1.9.0", features = ["rt", "macros"]}

Expand Down
17 changes: 16 additions & 1 deletion gui/src/app/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use tracing_subscriber::filter;

/// Config required to start internal bitcoind.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct InternalBitcoindExeConfig {
/// Internal bitcoind executable path.
pub exe_path: PathBuf,
/// Internal bitcoind data dir.
pub data_dir: PathBuf,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Config {
/// Path to lianad configuration file.
Expand All @@ -16,18 +25,24 @@ pub struct Config {
/// hardware wallets config.
/// LEGACY: Use Settings module instead.
pub hardware_wallets: Option<Vec<HardwareWalletConfig>>,
/// Internal bitcoind executable config.
pub internal_bitcoind_exe_config: Option<InternalBitcoindExeConfig>,
}

pub const DEFAULT_FILE_NAME: &str = "gui.toml";

impl Config {
pub fn new(daemon_config_path: PathBuf) -> Self {
pub fn new(
daemon_config_path: PathBuf,
internal_bitcoind_exe_config: Option<InternalBitcoindExeConfig>,
) -> Self {
Self {
daemon_config_path: Some(daemon_config_path),
daemon_rpc_path: None,
log_level: None,
debug: None,
hardware_wallets: None,
internal_bitcoind_exe_config,
}
}

Expand Down
8 changes: 8 additions & 0 deletions gui/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use state::{

use crate::{
app::{cache::Cache, error::Error, menu::Menu, wallet::Wallet},
bitcoind::stop_internal_bitcoind,
daemon::{embedded::EmbeddedDaemon, Daemon},
};

Expand Down Expand Up @@ -115,6 +116,13 @@ 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);
}
}
}
}
}

Expand Down
77 changes: 77 additions & 0 deletions gui/src/bitcoind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use liana::{config::BitcoindConfig, miniscript::bitcoin};

use tracing::{info, warn};

use crate::app::config::InternalBitcoindExeConfig;

/// Possible errors when starting bitcoind.
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum StartInternalBitcoindError {
CommandError(String),
CouldNotCanonicalizeDataDir(String),
CouldNotCanonicalizeCookiePath(String),
CookieFileNotFound(String),
BitcoinDError(String),
}

impl std::fmt::Display for StartInternalBitcoindError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::CommandError(e) => {
write!(f, "Command to start bitcoind returned an error: {}", e)
}
Self::CouldNotCanonicalizeDataDir(e) => {
write!(f, "Failed to canonicalize datadir: {}", e)
}
Self::CouldNotCanonicalizeCookiePath(e) => {
write!(f, "Failed to canonicalize cookie path: {}", e)
}
Self::CookieFileNotFound(path) => {
write!(
f,
"Cookie file was not found at the expected path: {}",
path
)
}
Self::BitcoinDError(e) => write!(f, "bitcoind connection check failed: {}", e),
}
}
}

/// Start internal bitcoind for the given network.
pub fn start_internal_bitcoind(
network: &bitcoin::Network,
exe_config: InternalBitcoindExeConfig,
) -> Result<std::process::Child, StartInternalBitcoindError> {
let args = vec![
format!("-chain={}", network.to_core_arg()),
format!(
"-datadir={}",
exe_config
.data_dir
.canonicalize()
.map_err(|e| StartInternalBitcoindError::CouldNotCanonicalizeDataDir(
e.to_string()
))?
.to_string_lossy()
),
];
std::process::Command::new(exe_config.exe_path)
.args(&args)
.stdout(std::process::Stdio::null()) // We still get bitcoind's logs in debug.log.
.spawn()
.map_err(|e| StartInternalBitcoindError::CommandError(e.to_string()))
}

/// 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();
}
Err(e) => {
warn!("Could not create interface to internal bitcoind: '{}'.", e);
}
}
}
9 changes: 9 additions & 0 deletions gui/src/installer/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::time::Duration;

use crate::{
app::{
config::InternalBitcoindExeConfig,
settings::{KeySetting, Settings, WalletSetting},
wallet::DEFAULT_WALLET_NAME,
},
Expand All @@ -18,6 +19,8 @@ use liana::{
miniscript::bitcoin,
};

use super::step::InternalBitcoindConfig;

#[derive(Clone)]
pub struct Context {
pub bitcoin_config: BitcoinConfig,
Expand All @@ -30,6 +33,9 @@ pub struct Context {
// In case a user entered a mnemonic,
// we dont want to override the generated signer with it.
pub recovered_signer: Option<Arc<Signer>>,
pub bitcoind_is_external: bool,
pub internal_bitcoind_config: Option<InternalBitcoindConfig>,
pub internal_bitcoind_exe_config: Option<InternalBitcoindExeConfig>,
}

impl Context {
Expand All @@ -46,6 +52,9 @@ impl Context {
data_dir,
hw_is_used: false,
recovered_signer: None,
bitcoind_is_external: true,
internal_bitcoind_config: None,
internal_bitcoind_exe_config: None,
}
}

Expand Down
15 changes: 15 additions & 0 deletions gui/src/installer/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub enum Message {
UseHotSigner,
Installed(Result<PathBuf, Error>),
Network(Network),
SelectBitcoindType(SelectBitcoindTypeMsg),
InternalBitcoind(InternalBitcoindMsg),
DefineBitcoind(DefineBitcoind),
DefineDescriptor(DefineDescriptor),
ImportXpub(usize, Result<DescriptorPublicKey, Error>),
Expand All @@ -43,6 +45,19 @@ pub enum DefineBitcoind {
PingBitcoind,
}

#[derive(Debug, Clone)]
pub enum SelectBitcoindTypeMsg {
UseExternal(bool),
}

#[derive(Debug, Clone)]
pub enum InternalBitcoindMsg {
Previous,
Reload,
DefineConfig,
Start,
}

#[derive(Debug, Clone)]
pub enum DefineDescriptor {
ImportDescriptor(String),
Expand Down
Loading

0 comments on commit 36cf85d

Please sign in to comment.