config.rs (5312B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025, 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 use std::net::SocketAddr; 18 19 use bitcoin::Amount; 20 use sqlx::postgres::PgConnectOptions; 21 use taler_api::{ 22 Serve, 23 config::{ApiCfg, DbCfg}, 24 }; 25 use taler_common::{ 26 config::{CfgErr, Config}, 27 map_config, 28 types::amount::Currency, 29 }; 30 31 use crate::{ 32 payto::{BtcWallet, FullBtcPayto}, 33 taler_utils::taler_to_btc, 34 }; 35 36 pub fn parse_db_cfg(cfg: &Config) -> Result<DbCfg, CfgErr> { 37 DbCfg::parse(cfg.section("depolymerizer-bitcoindb-postgres")) 38 } 39 40 pub fn parse_account_payto(cfg: &Config) -> Result<FullBtcPayto, CfgErr> { 41 let sect = cfg.section("depolymerizer-bitcoin"); 42 let wallet: BtcWallet = sect.parse("bitcoin wallet address", "WALLET").require()?; 43 let name = sect.cstr("NAME").require()?; 44 45 Ok(FullBtcPayto::new(wallet, &name)) 46 } 47 48 #[derive(Debug, Clone)] 49 pub enum RpcAuth { 50 Cookie(String), 51 Basic(String), 52 } 53 54 pub struct ServeCfg { 55 pub payto: FullBtcPayto, 56 pub serve: Serve, 57 pub wire_gateway: Option<ApiCfg>, 58 pub revenue: Option<ApiCfg>, 59 pub observability: Option<ApiCfg>, 60 pub currency: Currency, 61 pub lifetime: Option<u32>, 62 } 63 64 impl ServeCfg { 65 pub fn parse(cfg: &Config) -> Result<Self, CfgErr> { 66 let payto = parse_account_payto(cfg)?; 67 68 let s = cfg.section("depolymerizer-bitcoin-httpd"); 69 70 let lifetime = s.number("LIFETIME").opt()?.filter(|it| *it != 0); 71 72 let serve = Serve::parse(&s)?; 73 74 let sect = cfg.section("depolymerizer-bitcoin"); 75 Ok(Self { 76 currency: sect.parse("currency", "CURRENCY").require()?, 77 lifetime, 78 payto, 79 serve, 80 wire_gateway: ApiCfg::parse( 81 cfg.section("depolymerizer-bitcoin-httpd-wire-gateway-api"), 82 )?, 83 revenue: ApiCfg::parse(cfg.section("depolymerizer-bitcoin-httpd-revenue-api"))?, 84 observability: ApiCfg::parse( 85 cfg.section("depolymerizer-bitcoin-httpd-observability-api"), 86 )?, 87 }) 88 } 89 } 90 91 #[derive(Debug, Clone)] 92 93 pub struct WorkerCfg { 94 pub conf: u32, 95 pub max_conf: u32, 96 pub bounce_fee: Amount, 97 pub lifetime: Option<u32>, 98 pub bump_delay: Option<u32>, 99 pub db_config: PgConnectOptions, 100 pub currency: Currency, 101 pub rpc_cfg: RpcCfg, 102 pub wallet_cfg: WalletCfg, 103 } 104 105 impl WorkerCfg { 106 pub fn parse(cfg: &Config) -> Result<Self, CfgErr> { 107 let currency: Currency = cfg 108 .section("depolymerizer-bitcoin") 109 .parse("currency", "CURRENCY") 110 .require()?; 111 112 let sect = cfg.section("depolymerizer-bitcoin-worker"); 113 let confirmation = sect.number("CONFIRMATION").require()?; 114 115 Ok(Self { 116 conf: confirmation, 117 max_conf: confirmation * 2, 118 bounce_fee: sect 119 .amount("BOUNCE_FEE", ¤cy) 120 .opt()? 121 .map(|it| taler_to_btc(&it)) 122 .unwrap_or_default(), 123 lifetime: sect.number("LIFETIME").opt()?.filter(|it| *it != 0), 124 bump_delay: sect.number("BUMP_DELAY").opt()?.filter(|it| *it != 0), 125 db_config: cfg 126 .section("depolymerizer-bitcoindb-postgres") 127 .parse("Postgres", "CONFIG") 128 .require() 129 .unwrap(), 130 currency, 131 rpc_cfg: RpcCfg::parse(cfg)?, 132 wallet_cfg: WalletCfg::parse(cfg)?, 133 }) 134 } 135 } 136 137 #[derive(Debug, Clone)] 138 pub struct WalletCfg { 139 pub name: String, 140 pub password: Option<String>, 141 } 142 143 impl WalletCfg { 144 pub fn parse(cfg: &Config) -> Result<Self, CfgErr> { 145 let sect = cfg.section("depolymerizer-bitcoin-worker"); 146 let password = sect.str("PASSWORD").opt()?; 147 let name = sect.str("WALLET_NAME").require()?; 148 149 Ok(Self { name, password }) 150 } 151 } 152 153 #[derive(Debug, Clone)] 154 pub struct RpcCfg { 155 pub addr: SocketAddr, 156 pub auth: RpcAuth, 157 } 158 159 impl RpcCfg { 160 pub fn parse(cfg: &Config) -> Result<Self, CfgErr> { 161 let sect = cfg.section("depolymerizer-bitcoin-worker"); 162 163 let addr = sect.parse("RPC server address", "RPC_BIND").require()?; 164 165 let auth = map_config!(sect, "auth_method", "RPC_AUTH_METHOD", 166 "basic" => { 167 let username = sect.str("RPC_USERNAME").require()?; 168 let password = sect.str("RPC_PASSWORD").require()?; 169 RpcAuth::Basic(format!("{username}:{password}")) 170 }, 171 "cookie" => { 172 RpcAuth::Cookie(sect.path("RPC_COOKIE_FILE").require()?) 173 } 174 ) 175 .require()?; 176 177 Ok(Self { addr, auth }) 178 } 179 }