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 bitcoin::hashes::{sha256, Hash};
10use fedimint_core::config::FederationId;
11use fedimint_core::encoding::Encodable;
12use fedimint_core::task::TaskGroup;
13use fedimint_ln_common::gateway_endpoint_constants::{
14 GET_GATEWAY_ID_ENDPOINT, PAY_INVOICE_ENDPOINT,
15};
16use fedimint_lnv2_common::endpoint_constants::{
17 CREATE_BOLT11_INVOICE_ENDPOINT, ROUTING_INFO_ENDPOINT, SEND_PAYMENT_ENDPOINT,
18};
19use fedimint_lnv2_common::gateway_api::{CreateBolt11InvoicePayload, SendPaymentPayload};
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, CloseChannelsWithPeerPayload, ConnectFedPayload,
28 CreateInvoiceForOperatorPayload, DepositAddressPayload, InfoPayload, LeaveFedPayload,
29 OpenChannelPayload, PayInvoiceForOperatorPayload, ReceiveEcashPayload, SendOnchainPayload,
30 SetConfigurationPayload, SpendEcashPayload, WithdrawPayload, ADDRESS_ENDPOINT, BACKUP_ENDPOINT,
31 CLOSE_CHANNELS_WITH_PEER_ENDPOINT, CONFIGURATION_ENDPOINT, CONNECT_FED_ENDPOINT,
32 CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT, GATEWAY_INFO_ENDPOINT, GATEWAY_INFO_POST_ENDPOINT,
33 GET_BALANCES_ENDPOINT, GET_LN_ONCHAIN_ADDRESS_ENDPOINT, LEAVE_FED_ENDPOINT,
34 LIST_ACTIVE_CHANNELS_ENDPOINT, MNEMONIC_ENDPOINT, OPEN_CHANNEL_ENDPOINT,
35 PAY_INVOICE_FOR_OPERATOR_ENDPOINT, RECEIVE_ECASH_ENDPOINT, SEND_ONCHAIN_ENDPOINT,
36 SET_CONFIGURATION_ENDPOINT, SPEND_ECASH_ENDPOINT, STOP_ENDPOINT, V1_API_ENDPOINT,
37 WITHDRAW_ENDPOINT,
38};
39use crate::error::{AdminGatewayError, PublicGatewayError};
40use crate::rpc::ConfigPayload;
41use crate::Gateway;
42
43pub async fn run_webserver(gateway: Arc<Gateway>, task_group: TaskGroup) -> anyhow::Result<()> {
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 gateway_config = gateway
96 .clone_gateway_config()
97 .await
98 .ok_or(StatusCode::NOT_FOUND)?;
99 let gateway_hashed_password = gateway_config.hashed_password;
100 let password_salt = gateway_config.password_salt;
101 authenticate(gateway_hashed_password, password_salt, request, next).await
102}
103
104async fn auth_after_config_middleware(
109 Extension(gateway): Extension<Arc<Gateway>>,
110 request: Request,
111 next: Next,
112) -> Result<impl IntoResponse, StatusCode> {
113 let gateway_config = gateway.clone_gateway_config().await;
116 if gateway_config.is_none() {
117 return Ok(next.run(request).await);
118 }
119
120 let gateway_config = gateway_config.expect("Already validated the gateway config is not none");
123 let gateway_hashed_password = gateway_config.hashed_password;
124 let password_salt = gateway_config.password_salt;
125 authenticate(gateway_hashed_password, password_salt, request, next).await
126}
127
128async fn authenticate(
130 gateway_hashed_password: sha256::Hash,
131 password_salt: [u8; 16],
132 request: Request,
133 next: Next,
134) -> Result<axum::response::Response, StatusCode> {
135 let token = extract_bearer_token(&request)?;
136 let hashed_password = hash_password(&token, password_salt);
137 if gateway_hashed_password == hashed_password {
138 return Ok(next.run(request).await);
139 }
140
141 Err(StatusCode::UNAUTHORIZED)
142}
143
144fn lnv1_routes() -> Router {
146 Router::new()
147 .route(PAY_INVOICE_ENDPOINT, post(pay_invoice))
148 .route(GET_GATEWAY_ID_ENDPOINT, get(get_gateway_id))
149}
150
151fn lnv2_routes() -> Router {
153 Router::new()
154 .route(ROUTING_INFO_ENDPOINT, post(routing_info_v2))
155 .route(SEND_PAYMENT_ENDPOINT, post(pay_bolt11_invoice_v2))
156 .route(
157 CREATE_BOLT11_INVOICE_ENDPOINT,
158 post(create_bolt11_invoice_v2),
159 )
160}
161
162fn v1_routes(gateway: Arc<Gateway>, task_group: TaskGroup) -> Router {
171 let mut public_routes = Router::new().route(RECEIVE_ECASH_ENDPOINT, post(receive_ecash));
173
174 if gateway.is_running_lnv1() {
175 public_routes = public_routes.merge(lnv1_routes());
176 }
177
178 if gateway.is_running_lnv2() {
179 public_routes = public_routes.merge(lnv2_routes());
180 }
181
182 let always_authenticated_routes = Router::new()
184 .route(ADDRESS_ENDPOINT, post(address))
185 .route(WITHDRAW_ENDPOINT, post(withdraw))
186 .route(CONNECT_FED_ENDPOINT, post(connect_fed))
187 .route(LEAVE_FED_ENDPOINT, post(leave_fed))
188 .route(BACKUP_ENDPOINT, post(backup))
189 .route(
190 CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT,
191 post(create_invoice_for_operator),
192 )
193 .route(
194 PAY_INVOICE_FOR_OPERATOR_ENDPOINT,
195 post(pay_invoice_operator),
196 )
197 .route(GET_LN_ONCHAIN_ADDRESS_ENDPOINT, get(get_ln_onchain_address))
198 .route(OPEN_CHANNEL_ENDPOINT, post(open_channel))
199 .route(
200 CLOSE_CHANNELS_WITH_PEER_ENDPOINT,
201 post(close_channels_with_peer),
202 )
203 .route(LIST_ACTIVE_CHANNELS_ENDPOINT, get(list_active_channels))
204 .route(SEND_ONCHAIN_ENDPOINT, post(send_onchain))
205 .route(GET_BALANCES_ENDPOINT, get(get_balances))
206 .route(SPEND_ECASH_ENDPOINT, post(spend_ecash))
207 .route(MNEMONIC_ENDPOINT, get(mnemonic))
208 .route(STOP_ENDPOINT, get(stop))
209 .layer(middleware::from_fn(auth_middleware));
210
211 let authenticated_after_config_routes = Router::new()
214 .route(SET_CONFIGURATION_ENDPOINT, post(set_configuration))
215 .route(CONFIGURATION_ENDPOINT, post(configuration))
216 .route(GATEWAY_INFO_POST_ENDPOINT, post(handle_post_info))
218 .route(GATEWAY_INFO_ENDPOINT, get(info))
219 .layer(middleware::from_fn(auth_after_config_middleware));
220
221 Router::new()
222 .merge(public_routes)
223 .merge(always_authenticated_routes)
224 .merge(authenticated_after_config_routes)
225 .layer(Extension(gateway))
226 .layer(Extension(task_group))
227 .layer(CorsLayer::permissive())
228}
229
230pub fn hash_password(plaintext_password: &str, salt: [u8; 16]) -> sha256::Hash {
233 let mut bytes = Vec::<u8>::new();
234 plaintext_password
235 .consensus_encode(&mut bytes)
236 .expect("Password is encodable");
237 salt.consensus_encode(&mut bytes)
238 .expect("Salt is encodable");
239 sha256::Hash::hash(&bytes)
240}
241
242#[instrument(skip_all, err)]
246async fn handle_post_info(
247 Extension(gateway): Extension<Arc<Gateway>>,
248 Json(_payload): Json<InfoPayload>,
249) -> Result<impl IntoResponse, AdminGatewayError> {
250 let info = gateway.handle_get_info().await?;
251 Ok(Json(json!(info)))
252}
253
254#[instrument(skip_all, err)]
256async fn info(
257 Extension(gateway): Extension<Arc<Gateway>>,
258) -> Result<impl IntoResponse, AdminGatewayError> {
259 let info = gateway.handle_get_info().await?;
260 Ok(Json(json!(info)))
261}
262
263#[instrument(skip_all, err, fields(?payload))]
265async fn configuration(
266 Extension(gateway): Extension<Arc<Gateway>>,
267 Json(payload): Json<ConfigPayload>,
268) -> Result<impl IntoResponse, AdminGatewayError> {
269 let gateway_fed_config = gateway
270 .handle_get_federation_config(payload.federation_id)
271 .await?;
272 Ok(Json(json!(gateway_fed_config)))
273}
274
275#[instrument(skip_all, err, fields(?payload))]
277async fn address(
278 Extension(gateway): Extension<Arc<Gateway>>,
279 Json(payload): Json<DepositAddressPayload>,
280) -> Result<impl IntoResponse, AdminGatewayError> {
281 let address = gateway.handle_address_msg(payload).await?;
282 Ok(Json(json!(address)))
283}
284
285#[instrument(skip_all, err, fields(?payload))]
287async fn withdraw(
288 Extension(gateway): Extension<Arc<Gateway>>,
289 Json(payload): Json<WithdrawPayload>,
290) -> Result<impl IntoResponse, AdminGatewayError> {
291 let txid = gateway.handle_withdraw_msg(payload).await?;
292 Ok(Json(json!(txid)))
293}
294
295#[instrument(skip_all, err, fields(?payload))]
296async fn create_invoice_for_operator(
297 Extension(gateway): Extension<Arc<Gateway>>,
298 Json(payload): Json<CreateInvoiceForOperatorPayload>,
299) -> Result<impl IntoResponse, AdminGatewayError> {
300 let invoice = gateway
301 .handle_create_invoice_for_operator_msg(payload)
302 .await?;
303 Ok(Json(json!(invoice)))
304}
305
306#[instrument(skip_all, err, fields(?payload))]
307async fn pay_invoice_operator(
308 Extension(gateway): Extension<Arc<Gateway>>,
309 Json(payload): Json<PayInvoiceForOperatorPayload>,
310) -> Result<impl IntoResponse, AdminGatewayError> {
311 let preimage = gateway.handle_pay_invoice_for_operator_msg(payload).await?;
312 Ok(Json(json!(preimage.0.encode_hex::<String>())))
313}
314
315#[instrument(skip_all, err, fields(?payload))]
316async fn pay_invoice(
317 Extension(gateway): Extension<Arc<Gateway>>,
318 Json(payload): Json<fedimint_ln_client::pay::PayInvoicePayload>,
319) -> Result<impl IntoResponse, PublicGatewayError> {
320 let preimage = gateway.handle_pay_invoice_msg(payload).await?;
321 Ok(Json(json!(preimage.0.encode_hex::<String>())))
322}
323
324#[instrument(skip_all, err, fields(?payload))]
326async fn connect_fed(
327 Extension(gateway): Extension<Arc<Gateway>>,
328 Json(payload): Json<ConnectFedPayload>,
329) -> Result<impl IntoResponse, AdminGatewayError> {
330 let fed = gateway.handle_connect_federation(payload).await?;
331 Ok(Json(json!(fed)))
332}
333
334#[instrument(skip_all, err, fields(?payload))]
336async fn leave_fed(
337 Extension(gateway): Extension<Arc<Gateway>>,
338 Json(payload): Json<LeaveFedPayload>,
339) -> Result<impl IntoResponse, AdminGatewayError> {
340 let fed = gateway.handle_leave_federation(payload).await?;
341 Ok(Json(json!(fed)))
342}
343
344#[instrument(skip_all, err, fields(?payload))]
346async fn backup(
347 Extension(gateway): Extension<Arc<Gateway>>,
348 Json(payload): Json<BackupPayload>,
349) -> Result<impl IntoResponse, AdminGatewayError> {
350 gateway.handle_backup_msg(payload).await?;
351 Ok(Json(json!(())))
352}
353
354#[instrument(skip_all, err, fields(?payload))]
355async fn set_configuration(
356 Extension(gateway): Extension<Arc<Gateway>>,
357 Json(payload): Json<SetConfigurationPayload>,
358) -> Result<impl IntoResponse, AdminGatewayError> {
359 gateway.handle_set_configuration_msg(payload).await?;
360 Ok(Json(json!(())))
361}
362
363#[instrument(skip_all, err)]
364async fn get_ln_onchain_address(
365 Extension(gateway): Extension<Arc<Gateway>>,
366) -> Result<impl IntoResponse, AdminGatewayError> {
367 let address = gateway.handle_get_ln_onchain_address_msg().await?;
368 Ok(Json(json!(address.to_string())))
369}
370
371#[instrument(skip_all, err, fields(?payload))]
372async fn open_channel(
373 Extension(gateway): Extension<Arc<Gateway>>,
374 Json(payload): Json<OpenChannelPayload>,
375) -> Result<impl IntoResponse, AdminGatewayError> {
376 let funding_txid = gateway.handle_open_channel_msg(payload).await?;
377 Ok(Json(json!(funding_txid)))
378}
379
380#[instrument(skip_all, err, fields(?payload))]
381async fn close_channels_with_peer(
382 Extension(gateway): Extension<Arc<Gateway>>,
383 Json(payload): Json<CloseChannelsWithPeerPayload>,
384) -> Result<impl IntoResponse, AdminGatewayError> {
385 let response = gateway.handle_close_channels_with_peer_msg(payload).await?;
386 Ok(Json(json!(response)))
387}
388
389#[instrument(skip_all, err)]
390async fn list_active_channels(
391 Extension(gateway): Extension<Arc<Gateway>>,
392) -> Result<impl IntoResponse, AdminGatewayError> {
393 let channels = gateway.handle_list_active_channels_msg().await?;
394 Ok(Json(json!(channels)))
395}
396
397#[instrument(skip_all, err)]
398async fn send_onchain(
399 Extension(gateway): Extension<Arc<Gateway>>,
400 Json(payload): Json<SendOnchainPayload>,
401) -> Result<impl IntoResponse, AdminGatewayError> {
402 let txid = gateway.handle_send_onchain_msg(payload).await?;
403 Ok(Json(json!(txid)))
404}
405
406#[instrument(skip_all, err)]
407async fn get_balances(
408 Extension(gateway): Extension<Arc<Gateway>>,
409) -> Result<impl IntoResponse, AdminGatewayError> {
410 let balances = gateway.handle_get_balances_msg().await?;
411 Ok(Json(json!(balances)))
412}
413
414#[instrument(skip_all, err)]
415async fn get_gateway_id(
416 Extension(gateway): Extension<Arc<Gateway>>,
417) -> Result<impl IntoResponse, PublicGatewayError> {
418 Ok(Json(json!(gateway.gateway_id)))
419}
420
421#[instrument(skip_all, err)]
422async fn routing_info_v2(
423 Extension(gateway): Extension<Arc<Gateway>>,
424 Json(federation_id): Json<FederationId>,
425) -> Result<impl IntoResponse, PublicGatewayError> {
426 let routing_info = gateway.routing_info_v2(&federation_id).await?;
427 Ok(Json(json!(routing_info)))
428}
429
430#[instrument(skip_all, err)]
431async fn pay_bolt11_invoice_v2(
432 Extension(gateway): Extension<Arc<Gateway>>,
433 Json(payload): Json<SendPaymentPayload>,
434) -> Result<impl IntoResponse, PublicGatewayError> {
435 let payment_result = gateway.send_payment_v2(payload).await?;
436 Ok(Json(json!(payment_result)))
437}
438
439#[instrument(skip_all, err)]
440async fn create_bolt11_invoice_v2(
441 Extension(gateway): Extension<Arc<Gateway>>,
442 Json(payload): Json<CreateBolt11InvoicePayload>,
443) -> Result<impl IntoResponse, PublicGatewayError> {
444 let invoice = gateway.create_bolt11_invoice_v2(payload).await?;
445 Ok(Json(json!(invoice)))
446}
447
448#[instrument(skip_all, err)]
449async fn spend_ecash(
450 Extension(gateway): Extension<Arc<Gateway>>,
451 Json(payload): Json<SpendEcashPayload>,
452) -> Result<impl IntoResponse, AdminGatewayError> {
453 Ok(Json(json!(gateway.handle_spend_ecash_msg(payload).await?)))
454}
455
456#[instrument(skip_all, err)]
457async fn receive_ecash(
458 Extension(gateway): Extension<Arc<Gateway>>,
459 Json(payload): Json<ReceiveEcashPayload>,
460) -> Result<impl IntoResponse, PublicGatewayError> {
461 Ok(Json(json!(
462 gateway.handle_receive_ecash_msg(payload).await?
463 )))
464}
465
466#[instrument(skip_all, err)]
467async fn mnemonic(
468 Extension(gateway): Extension<Arc<Gateway>>,
469) -> Result<impl IntoResponse, AdminGatewayError> {
470 let words = gateway.handle_mnemonic_msg().await?;
471 Ok(Json(json!(words)))
472}
473
474#[instrument(skip_all, err)]
475async fn stop(
476 Extension(task_group): Extension<TaskGroup>,
477 Extension(gateway): Extension<Arc<Gateway>>,
478) -> Result<impl IntoResponse, AdminGatewayError> {
479 gateway.handle_shutdown_msg(task_group).await?;
480 Ok(Json(json!(())))
481}