/* 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, }) } }