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 fedimint 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                        #[cfg(not(feature = "tor"))]
114                        use_tor: None,
115                        recover,
116                    })
117                    .await?;
118
119                print_response(response);
120            }
121            Self::LeaveFed { federation_id } => {
122                let response = create_client()
123                    .leave_federation(LeaveFedPayload { federation_id })
124                    .await?;
125                print_response(response);
126            }
127            Self::Seed => {
128                let response = create_client().get_mnemonic().await?;
129                print_response(response);
130            }
131            Self::Stop => {
132                create_client().stop().await?;
133            }
134            Self::PaymentLog {
135                end_position,
136                pagination_size,
137                federation_id,
138                event_kinds,
139            } => {
140                let payment_log = create_client()
141                    .payment_log(PaymentLogPayload {
142                        end_position,
143                        pagination_size,
144                        federation_id,
145                        event_kinds,
146                    })
147                    .await?;
148                print_response(payment_log);
149            }
150            Self::CreatePasswordHash { password, cost } => print_response(
151                bcrypt::hash(password, cost.unwrap_or(bcrypt::DEFAULT_COST))
152                    .expect("Unable to create bcrypt hash"),
153            ),
154            Self::PaymentSummary { start, end } => {
155                let now = now();
156                let now_millis = now
157                    .duration_since(UNIX_EPOCH)
158                    .expect("Before unix epoch")
159                    .as_millis()
160                    .try_into()?;
161                let one_day_ago = now
162                    .checked_sub(Duration::from_secs(60 * 60 * 24))
163                    .expect("Before unix epoch");
164                let one_day_ago_millis = one_day_ago
165                    .duration_since(UNIX_EPOCH)
166                    .expect("Before unix epoch")
167                    .as_millis()
168                    .try_into()?;
169                let end_millis = end.unwrap_or(now_millis);
170                let start_millis = start.unwrap_or(one_day_ago_millis);
171                let payment_summary = create_client()
172                    .payment_summary(PaymentSummaryPayload {
173                        start_millis,
174                        end_millis,
175                    })
176                    .await?;
177                print_response(payment_summary);
178            }
179        }
180
181        Ok(())
182    }
183}