fedimint_lnv2_client/
cli.rs

1use std::{ffi, iter};
2
3use clap::{Parser, Subcommand};
4use fedimint_core::core::OperationId;
5use fedimint_core::util::SafeUrl;
6use fedimint_core::{Amount, PeerId};
7use lightning_invoice::Bolt11Invoice;
8use serde::Serialize;
9use serde_json::Value;
10
11use crate::api::LightningFederationApi;
12use crate::{Bolt11InvoiceDescription, LightningClientModule};
13
14#[derive(Parser, Serialize)]
15enum Opts {
16    /// Pay an invoice. For  testing  you can optionally specify a gateway to
17    /// route with, otherwise a gateway will be selected automatically.
18    Send {
19        invoice: Bolt11Invoice,
20        #[arg(long)]
21        gateway: Option<SafeUrl>,
22    },
23    /// Await the final state of the send operation.
24    AwaitSend { operation_id: OperationId },
25    /// Request an invoice. For testing you can optionally specify a gateway to
26    /// generate the invoice, otherwise a gateway will be selected
27    /// automatically.
28    Receive {
29        amount: Amount,
30        #[arg(long)]
31        gateway: Option<SafeUrl>,
32    },
33    /// Await the final state of the receive operation.
34    AwaitReceive { operation_id: OperationId },
35    /// Lnurl subcommands
36    #[command(subcommand)]
37    Lnurl(LnurlOpts),
38    /// Gateway subcommands
39    #[command(subcommand)]
40    Gateways(GatewaysOpts),
41}
42
43#[derive(Clone, Subcommand, Serialize)]
44enum LnurlOpts {
45    /// Generate a new lnurl.
46    Generate {
47        recurringd: SafeUrl,
48        #[arg(long)]
49        gateway: Option<SafeUrl>,
50    },
51    /// Receive an lnurl.
52    Receive {
53        #[arg(long)]
54        batch_size: Option<usize>,
55    },
56}
57
58#[derive(Clone, Subcommand, Serialize)]
59enum GatewaysOpts {
60    /// Update the mapping from lightning node public keys to gateway api
61    /// endpoints maintained in the module database to optimise gateway
62    /// selection for a given invoice; this command is intended for testing.
63    Map,
64    /// Select an online vetted gateway; this command is intended for testing.
65    Select {
66        #[arg(long)]
67        invoice: Option<Bolt11Invoice>,
68    },
69    /// List all vetted gateways.
70    List {
71        #[arg(long)]
72        peer: Option<PeerId>,
73    },
74    /// Add a vetted gateway.
75    Add { gateway: SafeUrl },
76    /// Remove a vetted gateway.
77    Remove { gateway: SafeUrl },
78}
79
80pub(crate) async fn handle_cli_command(
81    lightning: &LightningClientModule,
82    args: &[ffi::OsString],
83) -> anyhow::Result<serde_json::Value> {
84    let opts = Opts::parse_from(iter::once(&ffi::OsString::from("lnv2")).chain(args.iter()));
85
86    let value = match opts {
87        Opts::Send { gateway, invoice } => {
88            json(lightning.send(invoice, gateway, Value::Null).await?)
89        }
90        Opts::AwaitSend { operation_id } => json(
91            lightning
92                .await_final_send_operation_state(operation_id)
93                .await?,
94        ),
95        Opts::Receive { amount, gateway } => json(
96            lightning
97                .receive(
98                    amount,
99                    3600,
100                    Bolt11InvoiceDescription::Direct(String::new()),
101                    gateway,
102                    Value::Null,
103                )
104                .await?,
105        ),
106        Opts::AwaitReceive { operation_id } => json(
107            lightning
108                .await_final_receive_operation_state(operation_id)
109                .await?,
110        ),
111        Opts::Lnurl(lnurl_opts) => match lnurl_opts {
112            LnurlOpts::Generate {
113                recurringd,
114                gateway,
115            } => json(lightning.generate_lnurl(recurringd, gateway).await?),
116            LnurlOpts::Receive { batch_size } => {
117                json(lightning.receive_lnurl(batch_size, Value::Null).await)
118            }
119        },
120        Opts::Gateways(gateway_opts) => match gateway_opts {
121            #[allow(clippy::unit_arg)]
122            GatewaysOpts::Map => json(
123                LightningClientModule::update_gateway_map(
124                    &lightning.federation_id,
125                    &lightning.client_ctx,
126                    &lightning.module_api,
127                    &lightning.gateway_conn,
128                )
129                .await,
130            ),
131            GatewaysOpts::Select { invoice } => json(lightning.select_gateway(invoice).await?.0),
132            GatewaysOpts::List { peer } => match peer {
133                Some(peer) => json(lightning.module_api.gateways_from_peer(peer).await?),
134                None => json(lightning.module_api.gateways().await?),
135            },
136            GatewaysOpts::Add { gateway } => {
137                let auth = lightning
138                    .admin_auth
139                    .clone()
140                    .ok_or(anyhow::anyhow!("Admin auth not set"))?;
141
142                json(lightning.module_api.add_gateway(auth, gateway).await?)
143            }
144            GatewaysOpts::Remove { gateway } => {
145                let auth = lightning
146                    .admin_auth
147                    .clone()
148                    .ok_or(anyhow::anyhow!("Admin auth not set"))?;
149
150                json(lightning.module_api.remove_gateway(auth, gateway).await?)
151            }
152        },
153    };
154
155    Ok(value)
156}
157
158fn json<T: Serialize>(value: T) -> Value {
159    serde_json::to_value(value).expect("JSON serialization failed")
160}