depolymerization

wire gateway for Bitcoin/Ethereum
Log | Files | Refs | Submodules | README | LICENSE

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", &currency)
    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 }