depolymerization

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

cli.rs (4102B)


      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                     std::process::exit(1);
    102                 }
    103             } else {
    104                 let pool = pool(cfg).await?;
    105                 let cfg = ServeCfg::parse(cfg)?;
    106                 let api = ServerState::start(pool, cfg.payto, cfg.currency).await;
    107                 let mut router = Router::new();
    108                 if let Some(cfg) = cfg.wire_gateway {
    109                     router = router
    110                         .wire_gateway(api.clone(), cfg.auth.method())
    111                         .prepared_transfer(api.clone())
    112                 }
    113                 if let Some(cfg) = cfg.revenue {
    114                     router = router.revenue(api.clone(), cfg.auth.method());
    115                 }
    116 
    117                 router
    118                     .layer(middleware::from_fn_with_state(api, status_middleware))
    119                     .serve(&cfg.serve, cfg.lifetime)
    120                     .await?;
    121             }
    122         }
    123         Command::Config(cfg_cmd) => cfg_cmd.run(cfg)?,
    124     }
    125     Ok(())
    126 }