1use std::sync::Arc;
2
3use axum::extract::Request;
4use axum::http::{header, StatusCode};
5use axum::middleware::{self, Next};
6use axum::response::IntoResponse;
7use axum::routing::{get, post};
8use axum::{Extension, Json, Router};
9use fedimint_core::config::FederationId;
10use fedimint_core::task::TaskGroup;
11use fedimint_lightning::{CloseChannelsWithPeerRequest, OpenChannelRequest, SendOnchainRequest};
12use fedimint_ln_common::gateway_endpoint_constants::{
13 GET_GATEWAY_ID_ENDPOINT, PAY_INVOICE_ENDPOINT,
14};
15use fedimint_lnv2_common::endpoint_constants::{
16 CREATE_BOLT11_INVOICE_ENDPOINT, ROUTING_INFO_ENDPOINT, SEND_PAYMENT_ENDPOINT,
17};
18use fedimint_lnv2_common::gateway_api::{CreateBolt11InvoicePayload, SendPaymentPayload};
19use fedimint_logging::LOG_GATEWAY;
20use hex::ToHex;
21use serde_json::json;
22use tokio::net::TcpListener;
23use tower_http::cors::CorsLayer;
24use tracing::{error, info, instrument};
25
26use super::{
27 BackupPayload, ConnectFedPayload, CreateInvoiceForOperatorPayload, DepositAddressPayload,
28 DepositAddressRecheckPayload, InfoPayload, LeaveFedPayload, PayInvoiceForOperatorPayload,
29 PaymentLogPayload, ReceiveEcashPayload, SetFeesPayload, SpendEcashPayload, WithdrawPayload,
30 ADDRESS_ENDPOINT, ADDRESS_RECHECK_ENDPOINT, BACKUP_ENDPOINT, CLOSE_CHANNELS_WITH_PEER_ENDPOINT,
31 CONFIGURATION_ENDPOINT, CONNECT_FED_ENDPOINT, CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT,
32 GATEWAY_INFO_ENDPOINT, GATEWAY_INFO_POST_ENDPOINT, GET_BALANCES_ENDPOINT,
33 GET_LN_ONCHAIN_ADDRESS_ENDPOINT, LEAVE_FED_ENDPOINT, LIST_ACTIVE_CHANNELS_ENDPOINT,
34 MNEMONIC_ENDPOINT, OPEN_CHANNEL_ENDPOINT, PAYMENT_LOG_ENDPOINT,
35 PAY_INVOICE_FOR_OPERATOR_ENDPOINT, RECEIVE_ECASH_ENDPOINT, SEND_ONCHAIN_ENDPOINT,
36 SET_FEES_ENDPOINT, SPEND_ECASH_ENDPOINT, STOP_ENDPOINT, V1_API_ENDPOINT, WITHDRAW_ENDPOINT,
37};
38use crate::error::{AdminGatewayError, PublicGatewayError};
39use crate::rpc::ConfigPayload;
40use crate::Gateway;
41
42pub async fn run_webserver(gateway: Arc<Gateway>) -> anyhow::Result<()> {
44 let task_group = gateway.task_group.clone();
45 let v1_routes = v1_routes(gateway.clone(), task_group.clone());
46 let api_v1 = Router::new()
47 .nest(&format!("/{V1_API_ENDPOINT}"), v1_routes.clone())
48 .merge(v1_routes);
50
51 let handle = task_group.make_handle();
52 let shutdown_rx = handle.make_shutdown_rx();
53 let listener = TcpListener::bind(&gateway.listen).await?;
54 let serve = axum::serve(listener, api_v1.into_make_service());
55 task_group.spawn("Gateway Webserver", |_| async {
56 let graceful = serve.with_graceful_shutdown(async {
57 shutdown_rx.await;
58 });
59
60 if let Err(e) = graceful.await {
61 error!("Error shutting down gatewayd webserver: {:?}", e);
62 } else {
63 info!("Successfully shutdown webserver");
64 }
65 });
66
67 info!("Successfully started webserver on {}", gateway.listen);
68 Ok(())
69}
70
71fn extract_bearer_token(request: &Request) -> Result<String, StatusCode> {
73 let headers = request.headers();
74 let auth_header = headers.get(header::AUTHORIZATION);
75 if let Some(header_value) = auth_header {
76 let auth_str = header_value
77 .to_str()
78 .map_err(|_| StatusCode::UNAUTHORIZED)?;
79 let token = auth_str.trim_start_matches("Bearer ").to_string();
80 return Ok(token);
81 }
82
83 Err(StatusCode::UNAUTHORIZED)
84}
85
86async fn auth_middleware(
90 Extension(gateway): Extension<Arc<Gateway>>,
91 request: Request,
92 next: Next,
93) -> Result<impl IntoResponse, StatusCode> {
94 let token = extract_bearer_token(&request)?;
95 if bcrypt::verify(token, &gateway.bcrypt_password_hash.to_string())
96 .expect("Bcrypt hash is valid since we just stringified it")
97 {
98 return Ok(next.run(request).await);
99 }
100
101 Err(StatusCode::UNAUTHORIZED)
102}
103
104fn lnv1_routes() -> Router {
106 Router::new()
107 .route(PAY_INVOICE_ENDPOINT, post(pay_invoice))
108 .route(GET_GATEWAY_ID_ENDPOINT, get(get_gateway_id))
109}
110
111fn lnv2_routes() -> Router {
113 Router::new()
114 .route(ROUTING_INFO_ENDPOINT, post(routing_info_v2))
115 .route(SEND_PAYMENT_ENDPOINT, post(pay_bolt11_invoice_v2))
116 .route(
117 CREATE_BOLT11_INVOICE_ENDPOINT,
118 post(create_bolt11_invoice_v2),
119 )
120}
121
122fn v1_routes(gateway: Arc<Gateway>, task_group: TaskGroup) -> Router {
131 let mut public_routes = Router::new().route(RECEIVE_ECASH_ENDPOINT, post(receive_ecash));
133
134 if gateway.is_running_lnv1() {
135 public_routes = public_routes.merge(lnv1_routes());
136 }
137
138 if gateway.is_running_lnv2() {
139 public_routes = public_routes.merge(lnv2_routes());
140 }
141
142 let authenticated_routes = Router::new()
144 .route(ADDRESS_ENDPOINT, post(address))
145 .route(WITHDRAW_ENDPOINT, post(withdraw))
146 .route(CONNECT_FED_ENDPOINT, post(connect_fed))
147 .route(LEAVE_FED_ENDPOINT, post(leave_fed))
148 .route(BACKUP_ENDPOINT, post(backup))
149 .route(
150 CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT,
151 post(create_invoice_for_operator),
152 )
153 .route(
154 PAY_INVOICE_FOR_OPERATOR_ENDPOINT,
155 post(pay_invoice_operator),
156 )
157 .route(GET_LN_ONCHAIN_ADDRESS_ENDPOINT, get(get_ln_onchain_address))
158 .route(OPEN_CHANNEL_ENDPOINT, post(open_channel))
159 .route(
160 CLOSE_CHANNELS_WITH_PEER_ENDPOINT,
161 post(close_channels_with_peer),
162 )
163 .route(LIST_ACTIVE_CHANNELS_ENDPOINT, get(list_active_channels))
164 .route(SEND_ONCHAIN_ENDPOINT, post(send_onchain))
165 .route(ADDRESS_RECHECK_ENDPOINT, post(recheck_address))
166 .route(GET_BALANCES_ENDPOINT, get(get_balances))
167 .route(SPEND_ECASH_ENDPOINT, post(spend_ecash))
168 .route(MNEMONIC_ENDPOINT, get(mnemonic))
169 .route(STOP_ENDPOINT, get(stop))
170 .route(PAYMENT_LOG_ENDPOINT, post(payment_log))
171 .route(SET_FEES_ENDPOINT, post(set_fees))
172 .route(CONFIGURATION_ENDPOINT, post(configuration))
173 .route(GATEWAY_INFO_POST_ENDPOINT, post(handle_post_info))
175 .route(GATEWAY_INFO_ENDPOINT, get(info))
176 .layer(middleware::from_fn(auth_middleware));
177
178 Router::new()
179 .merge(public_routes)
180 .merge(authenticated_routes)
181 .layer(Extension(gateway))
182 .layer(Extension(task_group))
183 .layer(CorsLayer::permissive())
184}
185
186#[instrument(target = LOG_GATEWAY, skip_all, err)]
190async fn handle_post_info(
191 Extension(gateway): Extension<Arc<Gateway>>,
192 Json(_payload): Json<InfoPayload>,
193) -> Result<impl IntoResponse, AdminGatewayError> {
194 let info = gateway.handle_get_info().await?;
195 Ok(Json(json!(info)))
196}
197
198#[instrument(target = LOG_GATEWAY, skip_all, err)]
200async fn info(
201 Extension(gateway): Extension<Arc<Gateway>>,
202) -> Result<impl IntoResponse, AdminGatewayError> {
203 let info = gateway.handle_get_info().await?;
204 Ok(Json(json!(info)))
205}
206
207#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
209async fn configuration(
210 Extension(gateway): Extension<Arc<Gateway>>,
211 Json(payload): Json<ConfigPayload>,
212) -> Result<impl IntoResponse, AdminGatewayError> {
213 let gateway_fed_config = gateway
214 .handle_get_federation_config(payload.federation_id)
215 .await?;
216 Ok(Json(json!(gateway_fed_config)))
217}
218
219#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
221async fn address(
222 Extension(gateway): Extension<Arc<Gateway>>,
223 Json(payload): Json<DepositAddressPayload>,
224) -> Result<impl IntoResponse, AdminGatewayError> {
225 let address = gateway.handle_address_msg(payload).await?;
226 Ok(Json(json!(address)))
227}
228
229#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
231async fn withdraw(
232 Extension(gateway): Extension<Arc<Gateway>>,
233 Json(payload): Json<WithdrawPayload>,
234) -> Result<impl IntoResponse, AdminGatewayError> {
235 let txid = gateway.handle_withdraw_msg(payload).await?;
236 Ok(Json(json!(txid)))
237}
238
239#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
240async fn create_invoice_for_operator(
241 Extension(gateway): Extension<Arc<Gateway>>,
242 Json(payload): Json<CreateInvoiceForOperatorPayload>,
243) -> Result<impl IntoResponse, AdminGatewayError> {
244 let invoice = gateway
245 .handle_create_invoice_for_operator_msg(payload)
246 .await?;
247 Ok(Json(json!(invoice)))
248}
249
250#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
251async fn pay_invoice_operator(
252 Extension(gateway): Extension<Arc<Gateway>>,
253 Json(payload): Json<PayInvoiceForOperatorPayload>,
254) -> Result<impl IntoResponse, AdminGatewayError> {
255 let preimage = gateway.handle_pay_invoice_for_operator_msg(payload).await?;
256 Ok(Json(json!(preimage.0.encode_hex::<String>())))
257}
258
259#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
260async fn pay_invoice(
261 Extension(gateway): Extension<Arc<Gateway>>,
262 Json(payload): Json<fedimint_ln_client::pay::PayInvoicePayload>,
263) -> Result<impl IntoResponse, PublicGatewayError> {
264 let preimage = gateway.handle_pay_invoice_msg(payload).await?;
265 Ok(Json(json!(preimage.0.encode_hex::<String>())))
266}
267
268#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
270async fn connect_fed(
271 Extension(gateway): Extension<Arc<Gateway>>,
272 Json(payload): Json<ConnectFedPayload>,
273) -> Result<impl IntoResponse, AdminGatewayError> {
274 let fed = gateway.handle_connect_federation(payload).await?;
275 Ok(Json(json!(fed)))
276}
277
278#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
280async fn leave_fed(
281 Extension(gateway): Extension<Arc<Gateway>>,
282 Json(payload): Json<LeaveFedPayload>,
283) -> Result<impl IntoResponse, AdminGatewayError> {
284 let fed = gateway.handle_leave_federation(payload).await?;
285 Ok(Json(json!(fed)))
286}
287
288#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
290async fn backup(
291 Extension(gateway): Extension<Arc<Gateway>>,
292 Json(payload): Json<BackupPayload>,
293) -> Result<impl IntoResponse, AdminGatewayError> {
294 gateway.handle_backup_msg(payload).await?;
295 Ok(Json(json!(())))
296}
297
298#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
299async fn set_fees(
300 Extension(gateway): Extension<Arc<Gateway>>,
301 Json(payload): Json<SetFeesPayload>,
302) -> Result<impl IntoResponse, AdminGatewayError> {
303 gateway.handle_set_fees_msg(payload).await?;
304 Ok(Json(json!(())))
305}
306
307#[instrument(target = LOG_GATEWAY, skip_all, err)]
308async fn get_ln_onchain_address(
309 Extension(gateway): Extension<Arc<Gateway>>,
310) -> Result<impl IntoResponse, AdminGatewayError> {
311 let address = gateway.handle_get_ln_onchain_address_msg().await?;
312 Ok(Json(json!(address.to_string())))
313}
314
315#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
316async fn open_channel(
317 Extension(gateway): Extension<Arc<Gateway>>,
318 Json(payload): Json<OpenChannelRequest>,
319) -> Result<impl IntoResponse, AdminGatewayError> {
320 let funding_txid = gateway.handle_open_channel_msg(payload).await?;
321 Ok(Json(json!(funding_txid)))
322}
323
324#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
325async fn close_channels_with_peer(
326 Extension(gateway): Extension<Arc<Gateway>>,
327 Json(payload): Json<CloseChannelsWithPeerRequest>,
328) -> Result<impl IntoResponse, AdminGatewayError> {
329 let response = gateway.handle_close_channels_with_peer_msg(payload).await?;
330 Ok(Json(json!(response)))
331}
332
333#[instrument(target = LOG_GATEWAY, skip_all, err)]
334async fn list_active_channels(
335 Extension(gateway): Extension<Arc<Gateway>>,
336) -> Result<impl IntoResponse, AdminGatewayError> {
337 let channels = gateway.handle_list_active_channels_msg().await?;
338 Ok(Json(json!(channels)))
339}
340
341#[instrument(target = LOG_GATEWAY, skip_all, err)]
342async fn send_onchain(
343 Extension(gateway): Extension<Arc<Gateway>>,
344 Json(payload): Json<SendOnchainRequest>,
345) -> Result<impl IntoResponse, AdminGatewayError> {
346 let txid = gateway.handle_send_onchain_msg(payload).await?;
347 Ok(Json(json!(txid)))
348}
349
350#[instrument(target = LOG_GATEWAY, skip_all, err)]
351async fn recheck_address(
352 Extension(gateway): Extension<Arc<Gateway>>,
353 Json(payload): Json<DepositAddressRecheckPayload>,
354) -> Result<impl IntoResponse, AdminGatewayError> {
355 gateway.handle_recheck_address_msg(payload).await?;
356 Ok(Json(json!({})))
357}
358
359#[instrument(target = LOG_GATEWAY, skip_all, err)]
360async fn get_balances(
361 Extension(gateway): Extension<Arc<Gateway>>,
362) -> Result<impl IntoResponse, AdminGatewayError> {
363 let balances = gateway.handle_get_balances_msg().await?;
364 Ok(Json(json!(balances)))
365}
366
367#[instrument(target = LOG_GATEWAY, skip_all, err)]
368async fn get_gateway_id(
369 Extension(gateway): Extension<Arc<Gateway>>,
370) -> Result<impl IntoResponse, PublicGatewayError> {
371 Ok(Json(json!(gateway.gateway_id)))
372}
373
374#[instrument(target = LOG_GATEWAY, skip_all, err)]
375async fn routing_info_v2(
376 Extension(gateway): Extension<Arc<Gateway>>,
377 Json(federation_id): Json<FederationId>,
378) -> Result<impl IntoResponse, PublicGatewayError> {
379 let routing_info = gateway.routing_info_v2(&federation_id).await?;
380 Ok(Json(json!(routing_info)))
381}
382
383#[instrument(target = LOG_GATEWAY, skip_all, err)]
384async fn pay_bolt11_invoice_v2(
385 Extension(gateway): Extension<Arc<Gateway>>,
386 Json(payload): Json<SendPaymentPayload>,
387) -> Result<impl IntoResponse, PublicGatewayError> {
388 let payment_result = gateway.send_payment_v2(payload).await?;
389 Ok(Json(json!(payment_result)))
390}
391
392#[instrument(target = LOG_GATEWAY, skip_all, err)]
393async fn create_bolt11_invoice_v2(
394 Extension(gateway): Extension<Arc<Gateway>>,
395 Json(payload): Json<CreateBolt11InvoicePayload>,
396) -> Result<impl IntoResponse, PublicGatewayError> {
397 let invoice = gateway.create_bolt11_invoice_v2(payload).await?;
398 Ok(Json(json!(invoice)))
399}
400
401#[instrument(target = LOG_GATEWAY, skip_all, err)]
402async fn spend_ecash(
403 Extension(gateway): Extension<Arc<Gateway>>,
404 Json(payload): Json<SpendEcashPayload>,
405) -> Result<impl IntoResponse, AdminGatewayError> {
406 Ok(Json(json!(gateway.handle_spend_ecash_msg(payload).await?)))
407}
408
409#[instrument(target = LOG_GATEWAY, skip_all, err)]
410async fn receive_ecash(
411 Extension(gateway): Extension<Arc<Gateway>>,
412 Json(payload): Json<ReceiveEcashPayload>,
413) -> Result<impl IntoResponse, PublicGatewayError> {
414 Ok(Json(json!(
415 gateway.handle_receive_ecash_msg(payload).await?
416 )))
417}
418
419#[instrument(target = LOG_GATEWAY, skip_all, err)]
420async fn mnemonic(
421 Extension(gateway): Extension<Arc<Gateway>>,
422) -> Result<impl IntoResponse, AdminGatewayError> {
423 let words = gateway.handle_mnemonic_msg().await?;
424 Ok(Json(json!(words)))
425}
426
427#[instrument(target = LOG_GATEWAY, skip_all, err)]
428async fn stop(
429 Extension(task_group): Extension<TaskGroup>,
430 Extension(gateway): Extension<Arc<Gateway>>,
431) -> Result<impl IntoResponse, AdminGatewayError> {
432 gateway.handle_shutdown_msg(task_group).await?;
433 Ok(Json(json!(())))
434}
435
436#[instrument(target = LOG_GATEWAY, skip_all, err)]
437async fn payment_log(
438 Extension(gateway): Extension<Arc<Gateway>>,
439 Json(payload): Json<PaymentLogPayload>,
440) -> Result<impl IntoResponse, AdminGatewayError> {
441 let payment_log = gateway.handle_payment_log_msg(payload).await?;
442 Ok(Json(json!(payment_log)))
443}