depolymerization

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

cli.rs (4369B)


      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 axum::{Router, middleware};
     18 use taler_api::api::TalerRouter as _;
     19 use taler_build::long_version;
     20 use taler_common::{CommonArgs, cli::ConfigCmd, config::Config};
     21 use tracing::info;
     22 
     23 use crate::{
     24     api::{ServerState, status_middleware},
     25     config::{ServeCfg, WorkerCfg},
     26     db::{dbinit, pool},
     27     loops::{
     28         watcher::watcher,
     29         worker::{worker_loop, worker_transient},
     30     },
     31     setup::setup,
     32 };
     33 
     34 /// Taler adapter for bitcoincore
     35 #[derive(clap::Parser, Debug)]
     36 #[command(long_version = long_version(), about, long_about = None)]
     37 pub struct Args {
     38     #[clap(flatten)]
     39     pub common: CommonArgs,
     40     #[clap(subcommand)]
     41     pub cmd: Command,
     42 }
     43 
     44 #[derive(clap::Subcommand, Debug)]
     45 pub enum Command {
     46     /// Initialize btc-wire database
     47     Dbinit {
     48         /// Reset database (DANGEROUS: All existing data is lost)
     49         #[clap(long, short)]
     50         reset: bool,
     51     },
     52     /// Check worker configuration and setup worker state
     53     Setup {
     54         #[clap(long, short)]
     55         reset: bool,
     56     },
     57     /// Run btc-wire worker
     58     Worker {
     59         /// Execute once and return
     60         #[clap(long, short)]
     61         transient: bool,
     62     },
     63     /// Run btc-wire HTTP server
     64     Serve {
     65         /// Check whether an API is in use (if it's useful to start the HTTP
     66         /// server). Exit with 0 if at least one API is enabled, otherwise 1
     67         #[clap(long)]
     68         check: bool,
     69     },
     70     #[command(subcommand)]
     71     Config(ConfigCmd),
     72 }
     73 
     74 // TODO support external signer https://github.com/bitcoin/bitcoin/blob/master/doc/external-signer.md
     75 
     76 pub async fn run(cmd: Command, cfg: &Config) -> anyhow::Result<()> {
     77     match cmd {
     78         Command::Dbinit { reset } => {
     79             dbinit(cfg, reset).await?;
     80         }
     81         Command::Setup { reset } => {
     82             setup(cfg, reset).await?;
     83         }
     84         Command::Worker { transient } => {
     85             let state = WorkerCfg::parse(cfg)?;
     86             let pool = pool(cfg).await?;
     87 
     88             if transient {
     89                 worker_transient(state, pool).await?;
     90             } else {
     91                 tokio::spawn(watcher(state.rpc_cfg.clone(), pool.clone()));
     92                 worker_loop(state, pool.clone()).await;
     93 
     94                 info!("btc-wire stopped");
     95             }
     96         }
     97         Command::Serve { check } => {
     98             if check {
     99                 let cfg = ServeCfg::parse(cfg)?;
    100                 if cfg.wire_gateway.is_none()
    101                     && cfg.revenue.is_none()
    102                     && cfg.observability.is_none()
    103                 {
    104                     std::process::exit(1);
    105                 }
    106             } else {
    107                 let pool = pool(cfg).await?;
    108                 let cfg = ServeCfg::parse(cfg)?;
    109                 let api = ServerState::start(pool, cfg.payto, cfg.currency).await;
    110                 let mut router = Router::new();
    111                 if let Some(cfg) = cfg.wire_gateway {
    112                     router = router
    113                         .wire_gateway(api.clone(), cfg.auth.method())
    114                         .prepared_transfer(api.clone())
    115                 }
    116                 if let Some(cfg) = cfg.revenue {
    117                     router = router.revenue(api.clone(), cfg.auth.method());
    118                 }
    119                 if let Some(cfg) = cfg.observability {
    120                     router = router.observability(api.clone(), cfg.auth.method());
    121                 }
    122 
    123                 router
    124                     .layer(middleware::from_fn_with_state(api, status_middleware))
    125                     .serve(&cfg.serve, cfg.lifetime)
    126                     .await?;
    127             }
    128         }
    129         Command::Config(cfg_cmd) => cfg_cmd.run(cfg)?,
    130     }
    131     Ok(())
    132 }