gateway_cli/
general_commands.rs

1use std::time::{Duration, UNIX_EPOCH};
2
3use clap::Subcommand;
4use fedimint_core::config::FederationId;
5use fedimint_core::fedimint_build_code_version_env;
6use fedimint_core::time::now;
7use fedimint_eventlog::{EventKind, EventLogId};
8use fedimint_gateway_client::GatewayRpcClient;
9use fedimint_gateway_common::{
10    ConnectFedPayload, LeaveFedPayload, PaymentLogPayload, PaymentSummaryPayload,
11};
12
13use crate::print_response;
14
15#[derive(Subcommand)]
16pub enum GeneralCommands {
17    /// Display the version hash of the CLI.
18    VersionHash,
19    /// Display high-level information about the gateway.
20    Info,
21    /// Get the total on-chain, lightning, and eCash balances of the gateway.
22    GetBalances,
23    /// Register the gateway with a federation.
24    ConnectFed {
25        /// Invite code to connect to the federation
26        invite_code: String,
27        /// Activate usage of Tor (or not) as the connector for the federation
28        /// client
29        #[cfg(feature = "tor")]
30        use_tor: Option<bool>,
31        /// Indicates if the client should be recovered from a mnemonic
32        #[clap(long)]
33        recover: Option<bool>,
34    },
35    /// Leave a federation.
36    LeaveFed {
37        #[clap(long)]
38        federation_id: FederationId,
39    },
40    /// Prints the seed phrase for the gateway
41    Seed,
42    /// Safely stop the gateway
43    Stop,
44    /// List the transactions that the gateway has processed
45    PaymentLog {
46        #[clap(long)]
47        end_position: Option<EventLogId>,
48
49        #[clap(long, default_value_t = 25)]
50        pagination_size: usize,
51
52        #[clap(long)]
53        federation_id: FederationId,
54
55        #[clap(long)]
56        event_kinds: Vec<EventKind>,
57    },
58    /// Create a bcrypt hash of a password, for use in gateway deployment
59    CreatePasswordHash {
60        password: String,
61
62        /// The bcrypt cost factor to use when hashing the password
63        #[clap(long)]
64        cost: Option<u32>,
65    },
66    /// List a payment summary for the last day
67    PaymentSummary {
68        #[clap(long)]
69        start: Option<u64>,
70
71        #[clap(long)]
72        end: Option<u64>,
73    },
74}
75
76impl GeneralCommands {
77    #[allow(clippy::too_many_lines)]
78    pub async fn handle(
79        self,
80        create_client: impl Fn() -> GatewayRpcClient + Send + Sync,
81    ) -> anyhow::Result<()> {
82        match self {
83            Self::VersionHash => {
84                println!("{}", fedimint_build_code_version_env!());
85            }
86            Self::Info => {
87                // For backwards-compatibility, fallback to the original POST endpoint if the
88                // GET endpoint fails
89                // FIXME: deprecated >= 0.3.0
90                let client = create_client();
91                let response = match client.get_info().await {
92                    Ok(res) => res,
93                    Err(_) => client.get_info_legacy().await?,
94                };
95
96                print_response(response);
97            }
98            Self::GetBalances => {
99                let response = create_client().get_balances().await?;
100                print_response(response);
101            }
102            Self::ConnectFed {
103                invite_code,
104                #[cfg(feature = "tor")]
105                use_tor,
106                recover,
107            } => {
108                let response = create_client()
109                    .connect_federation(ConnectFedPayload {
110                        invite_code,
111                        #[cfg(feature = "tor")]
112                        use_tor,
113                        recover,
114                    })
115                    .await?;
116
117                print_response(response);
118            }
119            Self::LeaveFed { federation_id } => {
120                let response = create_client()
121                    .leave_federation(LeaveFedPayload { federation_id })
122                    .await?;
123                print_response(response);
124            }
125            Self::Seed => {
126                let response = create_client().get_mnemonic().await?;
127                print_response(response);
128            }
129            Self::Stop => {
130                create_client().stop().await?;
131            }
132            Self::PaymentLog {
133                end_position,
134                pagination_size,
135                federation_id,
136                event_kinds,
137            } => {
138                let payment_log = create_client()
139                    .payment_log(PaymentLogPayload {
140                        end_position,
141                        pagination_size,
142                        federation_id,
143                        event_kinds,
144                    })
145                    .await?;
146                print_response(payment_log);
147            }
148            Self::CreatePasswordHash { password, cost } => print_response(
149                bcrypt::hash(password, cost.unwrap_or(bcrypt::DEFAULT_COST))
150                    .expect("Unable to create bcrypt hash"),
151            ),
152            Self::PaymentSummary { start, end } => {
153                let now = now();
154                let now_millis = now
155                    .duration_since(UNIX_EPOCH)
156                    .expect("Before unix epoch")
157                    .as_millis()
158                    .try_into()?;
159                let one_day_ago = now
160                    .checked_sub(Duration::from_secs(60 * 60 * 24))
161                    .expect("Before unix epoch");
162                let one_day_ago_millis = one_day_ago
163                    .duration_since(UNIX_EPOCH)
164                    .expect("Before unix epoch")
165                    .as_millis()
166                    .try_into()?;
167                let end_millis = end.unwrap_or(now_millis);
168                let start_millis = start.unwrap_or(one_day_ago_millis);
169                let payment_summary = create_client()
170                    .payment_summary(PaymentSummaryPayload {
171                        start_millis,
172                        end_millis,
173                    })
174                    .await?;
175                print_response(payment_summary);
176            }
177        }
178
179        Ok(())
180    }
181}