/*
This file is part of TALER
Copyright (C) 2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, see
*/
use std::{
net::SocketAddr,
path::{Path, PathBuf},
str::FromStr,
};
use bitcoin::Network;
use common::{
currency::CurrencyBtc,
log::{fail, OrFail},
};
use crate::{
check_network_currency,
rpc_utils::{chain_dir, rpc_port},
};
pub const WIRE_WALLET_NAME: &str = "wire";
#[derive(Debug, Clone)]
pub enum BtcAuth {
Cookie(PathBuf),
Auth(String),
}
/// Bitcoin config relevant for btc-wire
#[derive(Debug, Clone)]
pub struct BitcoinConfig {
pub network: Network,
pub addr: SocketAddr,
pub auth: BtcAuth,
}
impl BitcoinConfig {
/// Load from bitcoin path
pub fn load(path: impl AsRef, currency: CurrencyBtc) -> Result {
let path = path.as_ref();
let conf = if path.is_dir() {
ini::Ini::load_from_file(path.join("bitcoin.conf"))
} else {
ini::Ini::load_from_file(path)
}?;
let main = conf.general_section();
if !main.contains_key("txindex") {
fail("require a bitcoind node running with 'txindex' option");
}
if !main.contains_key("maxtxfee") {
fail("require a bitcoind node running with 'maxtxfee' option");
}
let network = if let Some("1") = main.get("testnet") {
Network::Testnet
} else if let Some("1") = main.get("signet") {
Network::Signet
} else if let Some("1") = main.get("regtest") {
Network::Regtest
} else {
Network::Bitcoin
};
check_network_currency(network, currency);
let section = match network {
Network::Bitcoin => Some(main),
Network::Testnet => conf.section(Some("test")),
Network::Signet => conf.section(Some("signet")),
Network::Regtest => conf.section(Some("regtest")),
_ => unimplemented!(),
};
let port = if let Some(addr) = section.and_then(|s| s.get("rpcport")) {
addr.parse()
.or_fail(|_| "bitcoin config 'rpcport' is not a valid port number".into())
} else {
rpc_port(network)
};
let addr = if let Some(addr) = section.and_then(|s| s.get("rpcbind")) {
SocketAddr::from_str(addr)
.or_fail(|_| "bitcoin config 'rpcbind' is not a valid socket address".into())
} else {
([127, 0, 0, 1], port).into()
};
let auth = if let (Some(login), Some(passwd)) = (
section.and_then(|s| s.get("rpcuser")),
section.and_then(|s| s.get("rpcpassword")),
) {
BtcAuth::Auth(format!("{}:{}", login, passwd))
} else if let (Some(login), Some(passwd)) = (main.get("rpcuser"), main.get("rpcpassword")) {
BtcAuth::Auth(format!("{}:{}", login, passwd))
} else {
let cookie_file = if let Some(path) = section.and_then(|s| s.get("rpccookiefile")) {
path
} else if let Some(path) = main.get("rpccookiefile") {
path
} else {
".cookie"
}
.to_string();
let data_dir = if let Some(path) = section.and_then(|s| s.get("datadir")) {
PathBuf::from(path)
} else if let Some(path) = main.get("datadir") {
PathBuf::from(path)
} else if path.is_file() {
path.parent().unwrap().to_path_buf()
} else {
path.to_path_buf()
};
BtcAuth::Cookie(data_dir.join(chain_dir(network)).join(cookie_file))
};
Ok(Self {
network,
addr,
auth,
})
}
}