Skip to main content

fedimint_gateway_server/
rpc_server.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use anyhow::anyhow;
5use axum::extract::{Path, Query, Request};
6use axum::http::{StatusCode, header};
7use axum::middleware::{self, Next};
8use axum::response::IntoResponse;
9use axum::routing::{get, post};
10use axum::{Extension, Json, Router};
11use bitcoin::hashes::sha256;
12use fedimint_core::config::FederationId;
13use fedimint_core::task::TaskGroup;
14use fedimint_core::util::FmtCompact;
15use fedimint_gateway_common::{
16    ADDRESS_ENDPOINT, ADDRESS_RECHECK_ENDPOINT, BACKUP_ENDPOINT, BackupPayload,
17    CLOSE_CHANNELS_WITH_PEER_ENDPOINT, CONFIGURATION_ENDPOINT, CONNECT_FED_ENDPOINT,
18    CONNECT_PEER_ENDPOINT, CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT,
19    CREATE_BOLT12_OFFER_FOR_OPERATOR_ENDPOINT, CloseChannelsWithPeerRequest, ConfigPayload,
20    ConnectFedPayload, ConnectPeerRequest, CreateInvoiceForOperatorPayload, CreateOfferPayload,
21    DepositAddressPayload, DepositAddressRecheckPayload, GATEWAY_INFO_ENDPOINT,
22    GET_BALANCES_ENDPOINT, GET_INVOICE_ENDPOINT, GET_LN_ONCHAIN_ADDRESS_ENDPOINT,
23    GetInvoiceRequest, INVITE_CODES_ENDPOINT, LEAVE_FED_ENDPOINT, LIST_CHANNELS_ENDPOINT,
24    LIST_TRANSACTIONS_ENDPOINT, LeaveFedPayload, ListTransactionsPayload, MNEMONIC_ENDPOINT,
25    OPEN_CHANNEL_ENDPOINT, OPEN_CHANNEL_WITH_PUSH_ENDPOINT, OpenChannelRequest,
26    PAY_INVOICE_FOR_OPERATOR_ENDPOINT, PAY_OFFER_FOR_OPERATOR_ENDPOINT, PAYMENT_LOG_ENDPOINT,
27    PAYMENT_SUMMARY_ENDPOINT, PEGIN_FROM_ONCHAIN_ENDPOINT, PayInvoiceForOperatorPayload,
28    PayOfferPayload, PaymentLogPayload, PaymentSummaryPayload, PeginFromOnchainPayload,
29    RECEIVE_ECASH_ENDPOINT, ReceiveEcashPayload, SEND_ONCHAIN_ENDPOINT, SET_CHANNEL_FEES_ENDPOINT,
30    SET_FEES_ENDPOINT, SPEND_ECASH_ENDPOINT, STOP_ENDPOINT, SendOnchainRequest,
31    SetChannelFeesRequest, SetFeesPayload, SetMnemonicPayload, SpendEcashPayload, V1_API_ENDPOINT,
32    WITHDRAW_ENDPOINT, WITHDRAW_TO_ONCHAIN_ENDPOINT, WithdrawPayload, WithdrawToOnchainPayload,
33};
34use fedimint_gateway_ui::IAdminGateway;
35use fedimint_ln_common::gateway_endpoint_constants::{
36    GET_GATEWAY_ID_ENDPOINT, PAY_INVOICE_ENDPOINT,
37};
38use fedimint_lnurl::LnurlResponse;
39use fedimint_lnv2_common::endpoint_constants::{
40    CREATE_BOLT11_INVOICE_ENDPOINT, ROUTING_INFO_ENDPOINT, SEND_PAYMENT_ENDPOINT,
41};
42use fedimint_lnv2_common::gateway_api::{CreateBolt11InvoicePayload, SendPaymentPayload};
43use fedimint_logging::LOG_GATEWAY;
44use hex::ToHex;
45use serde::de::DeserializeOwned;
46use serde_json::json;
47use tokio::net::TcpListener;
48use tower_http::cors::CorsLayer;
49use tracing::{info, instrument, warn};
50
51use crate::error::{GatewayError, LnurlError};
52use crate::iroh_server::{Handlers, start_iroh_endpoint};
53use crate::{Gateway, GatewayState};
54
55// Routes that the liquidity manager is allowed to access. Any authenticated
56// route NOT in this list requires the admin password.
57const LIQUIDITY_MANAGER_ROUTES: [&str; 21] = [
58    ADDRESS_ENDPOINT,
59    ADDRESS_RECHECK_ENDPOINT,
60    CLOSE_CHANNELS_WITH_PEER_ENDPOINT,
61    CONFIGURATION_ENDPOINT,
62    CONNECT_PEER_ENDPOINT,
63    CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT,
64    CREATE_BOLT12_OFFER_FOR_OPERATOR_ENDPOINT,
65    GATEWAY_INFO_ENDPOINT,
66    GET_BALANCES_ENDPOINT,
67    GET_INVOICE_ENDPOINT,
68    GET_LN_ONCHAIN_ADDRESS_ENDPOINT,
69    INVITE_CODES_ENDPOINT,
70    LIST_CHANNELS_ENDPOINT,
71    LIST_TRANSACTIONS_ENDPOINT,
72    OPEN_CHANNEL_ENDPOINT,
73    PAYMENT_LOG_ENDPOINT,
74    PAYMENT_SUMMARY_ENDPOINT,
75    PEGIN_FROM_ONCHAIN_ENDPOINT,
76    SET_CHANNEL_FEES_ENDPOINT,
77    SET_FEES_ENDPOINT,
78    WITHDRAW_TO_ONCHAIN_ENDPOINT,
79];
80
81/// Creates the webserver's routes and spawns the webserver in a separate task.
82pub async fn run_webserver(
83    gateway: Arc<Gateway>,
84    mut mnemonic_receiver: tokio::sync::broadcast::Receiver<()>,
85) -> anyhow::Result<()> {
86    let task_group = gateway.task_group.clone();
87    let mut handlers = Handlers::new();
88
89    let routes = routes(gateway.clone(), task_group.clone(), &mut handlers);
90    // The UI router is merged separately from `routes` below and merged
91    // routers keep their own layers, so it needs its own
92    // `not_configured_middleware` layer. Without it, only the API routes are
93    // restricted to `is_allowed_setup_route` while the gateway is
94    // `NotConfigured`
95    let ui_routes = fedimint_gateway_ui::router(gateway.clone())
96        .layer(middleware::from_fn(not_configured_middleware))
97        .layer(Extension(gateway.clone()));
98    let api_v1 = Router::new()
99        .nest(&format!("/{V1_API_ENDPOINT}"), routes.clone())
100        // Backwards compatibility: Continue supporting gateway APIs without versioning
101        .merge(routes)
102        .merge(ui_routes);
103
104    let handle = task_group.make_handle();
105    let shutdown_rx = handle.make_shutdown_rx();
106    let listener = TcpListener::bind(&gateway.listen).await?;
107    let serve = axum::serve(listener, api_v1.into_make_service());
108    task_group.spawn("Gateway Webserver", |_| async {
109        let graceful = serve.with_graceful_shutdown(async {
110            shutdown_rx.await;
111        });
112
113        match graceful.await {
114            Err(err) => {
115                warn!(target: LOG_GATEWAY, err = %err.fmt_compact(), "Error shutting down gatewayd webserver");
116            }
117            _ => {
118                info!(target: LOG_GATEWAY, "Successfully shutdown webserver");
119            }
120        }
121    });
122    info!(target: LOG_GATEWAY, listen = %gateway.listen, "Successfully started webserver");
123
124    // Don't start the Iroh endpoint until the mnemonic has been set via HTTP or the
125    // UI
126    if let GatewayState::NotConfigured { .. } = gateway.get_state().await {
127        info!(target: LOG_GATEWAY, "Waiting for the mnemonic to be set before starting iroh loop.");
128        let _ = mnemonic_receiver.recv().await;
129    }
130
131    start_iroh_endpoint(&gateway, task_group, Arc::new(handlers)).await?;
132
133    Ok(())
134}
135
136/// Strips at most one leading `/v1` path segment so paths from both the
137/// versioned and unversioned API mounts can be compared against the unprefixed
138/// endpoint constants.
139fn strip_v1_prefix(path: &str) -> &str {
140    match path.strip_prefix(&format!("/{V1_API_ENDPOINT}")) {
141        Some(stripped) if stripped.starts_with('/') => stripped,
142        _ => path,
143    }
144}
145
146/// Extracts the Bearer token from the Authorization header of the request.
147fn extract_bearer_token(request: &Request) -> Result<String, StatusCode> {
148    let headers = request.headers();
149    let auth_header = headers.get(header::AUTHORIZATION);
150    if let Some(header_value) = auth_header {
151        let auth_str = header_value
152            .to_str()
153            .map_err(|_| StatusCode::UNAUTHORIZED)?;
154        let token = auth_str.trim_start_matches("Bearer ").to_string();
155        return Ok(token);
156    }
157
158    Err(StatusCode::UNAUTHORIZED)
159}
160
161async fn not_configured_middleware(
162    Extension(gateway): Extension<Arc<Gateway>>,
163    request: Request,
164    next: Next,
165) -> Result<impl IntoResponse, StatusCode> {
166    if matches!(
167        gateway.get_state().await,
168        GatewayState::NotConfigured { .. }
169    ) {
170        let method = request.method().clone();
171        let path = request.uri().path();
172
173        // Allow the API mnemonic endpoint (for CLI usage)
174        let is_mnemonic_api =
175            method == axum::http::Method::POST && strip_v1_prefix(path) == MNEMONIC_ENDPOINT;
176
177        let is_setup_route = fedimint_gateway_ui::is_allowed_setup_route(path);
178
179        if !is_mnemonic_api && !is_setup_route {
180            return Err(StatusCode::NOT_FOUND);
181        }
182    }
183
184    Ok(next.run(request).await)
185}
186
187/// Middleware to authenticate an incoming request. Routes that are
188/// authenticated with this middleware always require a Bearer token to be
189/// supplied in the Authorization header.
190async fn auth_middleware(
191    Extension(gateway): Extension<Arc<Gateway>>,
192    request: Request,
193    next: Next,
194) -> Result<impl IntoResponse, StatusCode> {
195    let token = extract_bearer_token(&request)?;
196    if bcrypt::verify(token.clone(), &gateway.bcrypt_password_hash)
197        .expect("Bcrypt hash is valid since we just stringified it")
198    {
199        return Ok(next.run(request).await);
200    }
201
202    // Check the liquidity manager
203    if let Some(liquidity_manager_password_hash) = &gateway.bcrypt_liquidity_manager_password_hash
204        && bcrypt::verify(token, liquidity_manager_password_hash)
205            .expect("Bcrypt hash is valid since we just stringified it")
206    {
207        let path = strip_v1_prefix(request.uri().path());
208
209        if !LIQUIDITY_MANAGER_ROUTES.contains(&path) {
210            return Err(StatusCode::UNAUTHORIZED);
211        }
212
213        return Ok(next.run(request).await);
214    }
215
216    Err(StatusCode::UNAUTHORIZED)
217}
218
219/// Registers a GET API handler for both the HTTP server and the Iroh
220/// `Endpoint`.
221fn register_get_handler<F, Fut>(
222    handlers: &mut Handlers,
223    route: &str,
224    func: F,
225    is_authenticated: bool,
226    router: Router,
227) -> Router
228where
229    F: Fn(Extension<Arc<Gateway>>) -> Fut + Clone + Send + Sync + 'static,
230    Fut: Future<Output = Result<Json<serde_json::Value>, GatewayError>> + Send + 'static,
231{
232    handlers.add_handler(route, func.clone(), is_authenticated);
233    router.route(route, get(func))
234}
235
236/// Registers a POST API handler for both the HTTP server and the Iroh
237/// `Endpoint`.
238fn register_post_handler<P, F, Fut>(
239    handlers: &mut Handlers,
240    route: &str,
241    func: F,
242    is_authenticated: bool,
243    router: Router,
244) -> Router
245where
246    P: DeserializeOwned + Send + 'static,
247    F: Fn(Extension<Arc<Gateway>>, Json<P>) -> Fut + Clone + Send + Sync + 'static,
248    Fut: Future<Output = Result<Json<serde_json::Value>, GatewayError>> + Send + 'static,
249{
250    handlers.add_handler_with_payload(route, func.clone(), is_authenticated);
251    router.route(route, post(func))
252}
253
254/// Public routes that are used in the LNv1 protocol
255fn lnv1_routes(handlers: &mut Handlers) -> Router {
256    let router = Router::new();
257    let router = register_post_handler(handlers, PAY_INVOICE_ENDPOINT, pay_invoice, false, router);
258    register_get_handler(
259        handlers,
260        GET_GATEWAY_ID_ENDPOINT,
261        get_gateway_id,
262        false,
263        router,
264    )
265}
266
267/// Public routes that are used in the LNv2 protocol
268fn lnv2_routes(handlers: &mut Handlers) -> Router {
269    let router = Router::new();
270    let router = register_post_handler(
271        handlers,
272        ROUTING_INFO_ENDPOINT,
273        routing_info_v2,
274        false,
275        router,
276    );
277    let router = register_post_handler(
278        handlers,
279        SEND_PAYMENT_ENDPOINT,
280        pay_bolt11_invoice_v2,
281        false,
282        router,
283    );
284    let router = register_post_handler(
285        handlers,
286        CREATE_BOLT11_INVOICE_ENDPOINT,
287        create_bolt11_invoice_v2,
288        false,
289        router,
290    );
291    // Verify endpoint does not have the same signature, it is handled separately
292    router.route("/verify/{payment_hash}", get(verify_bolt11_preimage_v2_get))
293}
294
295/// Gateway Webserver Routes. The gateway supports two types of routes
296/// - Always Authenticated: these routes always require a Bearer token. Used by
297///   gateway administrators.
298/// - Un-authenticated: anyone can request these routes. Used by fedimint
299///   clients.
300fn routes(gateway: Arc<Gateway>, task_group: TaskGroup, handlers: &mut Handlers) -> Router {
301    // Public routes on gateway webserver
302    let mut public_routes = register_post_handler(
303        handlers,
304        RECEIVE_ECASH_ENDPOINT,
305        receive_ecash,
306        false,
307        Router::new(),
308    );
309    public_routes = public_routes.merge(lnv1_routes(handlers));
310    public_routes = public_routes.merge(lnv2_routes(handlers));
311
312    // Authenticated routes used for gateway administration
313    let is_authenticated = true;
314    let authenticated_routes = Router::new();
315    let authenticated_routes = register_post_handler(
316        handlers,
317        ADDRESS_ENDPOINT,
318        address,
319        is_authenticated,
320        authenticated_routes,
321    );
322    let authenticated_routes = register_post_handler(
323        handlers,
324        WITHDRAW_ENDPOINT,
325        withdraw,
326        is_authenticated,
327        authenticated_routes,
328    );
329    let authenticated_routes = register_post_handler(
330        handlers,
331        WITHDRAW_TO_ONCHAIN_ENDPOINT,
332        withdraw_to_onchain,
333        is_authenticated,
334        authenticated_routes,
335    );
336    let authenticated_routes = register_post_handler(
337        handlers,
338        PEGIN_FROM_ONCHAIN_ENDPOINT,
339        pegin_from_onchain,
340        is_authenticated,
341        authenticated_routes,
342    );
343    let authenticated_routes = register_post_handler(
344        handlers,
345        CONNECT_FED_ENDPOINT,
346        connect_fed,
347        is_authenticated,
348        authenticated_routes,
349    );
350    let authenticated_routes = register_post_handler(
351        handlers,
352        LEAVE_FED_ENDPOINT,
353        leave_fed,
354        is_authenticated,
355        authenticated_routes,
356    );
357    let authenticated_routes = register_post_handler(
358        handlers,
359        BACKUP_ENDPOINT,
360        backup,
361        is_authenticated,
362        authenticated_routes,
363    );
364    let authenticated_routes = register_post_handler(
365        handlers,
366        CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT,
367        create_invoice_for_operator,
368        is_authenticated,
369        authenticated_routes,
370    );
371    let authenticated_routes = register_post_handler(
372        handlers,
373        CREATE_BOLT12_OFFER_FOR_OPERATOR_ENDPOINT,
374        create_offer_for_operator,
375        is_authenticated,
376        authenticated_routes,
377    );
378    let authenticated_routes = register_post_handler(
379        handlers,
380        PAY_INVOICE_FOR_OPERATOR_ENDPOINT,
381        pay_invoice_operator,
382        is_authenticated,
383        authenticated_routes,
384    );
385    let authenticated_routes = register_post_handler(
386        handlers,
387        PAY_OFFER_FOR_OPERATOR_ENDPOINT,
388        pay_offer_operator,
389        is_authenticated,
390        authenticated_routes,
391    );
392    let authenticated_routes = register_post_handler(
393        handlers,
394        GET_INVOICE_ENDPOINT,
395        get_invoice,
396        is_authenticated,
397        authenticated_routes,
398    );
399    let authenticated_routes = register_get_handler(
400        handlers,
401        GET_LN_ONCHAIN_ADDRESS_ENDPOINT,
402        get_ln_onchain_address,
403        is_authenticated,
404        authenticated_routes,
405    );
406    let authenticated_routes = register_post_handler(
407        handlers,
408        OPEN_CHANNEL_ENDPOINT,
409        open_channel,
410        is_authenticated,
411        authenticated_routes,
412    );
413    let authenticated_routes = register_post_handler(
414        handlers,
415        CONNECT_PEER_ENDPOINT,
416        connect_peer,
417        is_authenticated,
418        authenticated_routes,
419    );
420    let authenticated_routes = register_post_handler(
421        handlers,
422        OPEN_CHANNEL_WITH_PUSH_ENDPOINT,
423        open_channel_with_push,
424        is_authenticated,
425        authenticated_routes,
426    );
427    let authenticated_routes = register_post_handler(
428        handlers,
429        CLOSE_CHANNELS_WITH_PEER_ENDPOINT,
430        close_channels_with_peer,
431        is_authenticated,
432        authenticated_routes,
433    );
434    let authenticated_routes = register_get_handler(
435        handlers,
436        LIST_CHANNELS_ENDPOINT,
437        list_channels,
438        is_authenticated,
439        authenticated_routes,
440    );
441    let authenticated_routes = register_post_handler(
442        handlers,
443        SET_CHANNEL_FEES_ENDPOINT,
444        set_channel_fees,
445        is_authenticated,
446        authenticated_routes,
447    );
448    let authenticated_routes = register_post_handler(
449        handlers,
450        LIST_TRANSACTIONS_ENDPOINT,
451        list_transactions,
452        is_authenticated,
453        authenticated_routes,
454    );
455    let authenticated_routes = register_post_handler(
456        handlers,
457        SEND_ONCHAIN_ENDPOINT,
458        send_onchain,
459        is_authenticated,
460        authenticated_routes,
461    );
462    let authenticated_routes = register_post_handler(
463        handlers,
464        ADDRESS_RECHECK_ENDPOINT,
465        recheck_address,
466        is_authenticated,
467        authenticated_routes,
468    );
469    let authenticated_routes = register_get_handler(
470        handlers,
471        GET_BALANCES_ENDPOINT,
472        get_balances,
473        is_authenticated,
474        authenticated_routes,
475    );
476    let authenticated_routes = register_post_handler(
477        handlers,
478        SPEND_ECASH_ENDPOINT,
479        spend_ecash,
480        is_authenticated,
481        authenticated_routes,
482    );
483    let authenticated_routes = register_get_handler(
484        handlers,
485        MNEMONIC_ENDPOINT,
486        mnemonic,
487        is_authenticated,
488        authenticated_routes,
489    );
490    // Stop does not have the same function signature, it is handled separately
491    let authenticated_routes = authenticated_routes.route(STOP_ENDPOINT, get(stop));
492    let authenticated_routes = register_post_handler(
493        handlers,
494        PAYMENT_LOG_ENDPOINT,
495        payment_log,
496        is_authenticated,
497        authenticated_routes,
498    );
499    let authenticated_routes = register_post_handler(
500        handlers,
501        PAYMENT_SUMMARY_ENDPOINT,
502        payment_summary,
503        is_authenticated,
504        authenticated_routes,
505    );
506    let authenticated_routes = register_post_handler(
507        handlers,
508        SET_FEES_ENDPOINT,
509        set_fees,
510        is_authenticated,
511        authenticated_routes,
512    );
513    let authenticated_routes = register_post_handler(
514        handlers,
515        CONFIGURATION_ENDPOINT,
516        configuration,
517        is_authenticated,
518        authenticated_routes,
519    );
520    let authenticated_routes = register_get_handler(
521        handlers,
522        GATEWAY_INFO_ENDPOINT,
523        info,
524        is_authenticated,
525        authenticated_routes,
526    );
527    let authenticated_routes = register_post_handler(
528        handlers,
529        MNEMONIC_ENDPOINT,
530        set_mnemonic,
531        is_authenticated,
532        authenticated_routes,
533    );
534    let authenticated_routes = register_get_handler(
535        handlers,
536        INVITE_CODES_ENDPOINT,
537        invite_codes,
538        is_authenticated,
539        authenticated_routes,
540    );
541    let authenticated_routes = authenticated_routes.layer(middleware::from_fn(auth_middleware));
542
543    Router::new()
544        .merge(public_routes)
545        .merge(authenticated_routes)
546        .layer(middleware::from_fn(not_configured_middleware))
547        .layer(Extension(gateway))
548        .layer(Extension(task_group))
549        .layer(CorsLayer::permissive())
550}
551
552/// Display high-level information about the Gateway
553#[instrument(target = LOG_GATEWAY, skip_all, err)]
554async fn info(
555    Extension(gateway): Extension<Arc<Gateway>>,
556) -> Result<Json<serde_json::Value>, GatewayError> {
557    let info = gateway.handle_get_info().await?;
558    Ok(Json(json!(info)))
559}
560
561/// Display high-level information about the Gateway config
562#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
563async fn configuration(
564    Extension(gateway): Extension<Arc<Gateway>>,
565    Json(payload): Json<ConfigPayload>,
566) -> Result<Json<serde_json::Value>, GatewayError> {
567    let gateway_fed_config = gateway
568        .handle_get_federation_config(payload.federation_id)
569        .await?;
570    Ok(Json(json!(gateway_fed_config)))
571}
572
573/// Generate deposit address
574#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
575async fn address(
576    Extension(gateway): Extension<Arc<Gateway>>,
577    Json(payload): Json<DepositAddressPayload>,
578) -> Result<Json<serde_json::Value>, GatewayError> {
579    let address = gateway.handle_address_msg(payload).await?;
580    Ok(Json(json!(address)))
581}
582
583/// Pegs in funds from the gateway's onchain wallet
584#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
585async fn pegin_from_onchain(
586    Extension(gateway): Extension<Arc<Gateway>>,
587    Json(payload): Json<PeginFromOnchainPayload>,
588) -> Result<Json<serde_json::Value>, GatewayError> {
589    let address = gateway.handle_pegin_from_onchain_msg(payload).await?;
590    Ok(Json(json!(address)))
591}
592
593/// Withdraw from a gateway federation.
594#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
595async fn withdraw(
596    Extension(gateway): Extension<Arc<Gateway>>,
597    Json(payload): Json<WithdrawPayload>,
598) -> Result<Json<serde_json::Value>, GatewayError> {
599    let txid = gateway.handle_withdraw_msg(payload).await?;
600    Ok(Json(json!(txid)))
601}
602
603/// Withdraw from a gateway federation.
604#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
605async fn withdraw_to_onchain(
606    Extension(gateway): Extension<Arc<Gateway>>,
607    Json(payload): Json<WithdrawToOnchainPayload>,
608) -> Result<Json<serde_json::Value>, GatewayError> {
609    let txid = gateway.handle_withdraw_to_onchain_msg(payload).await?;
610    Ok(Json(json!(txid)))
611}
612
613#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
614async fn create_invoice_for_operator(
615    Extension(gateway): Extension<Arc<Gateway>>,
616    Json(payload): Json<CreateInvoiceForOperatorPayload>,
617) -> Result<Json<serde_json::Value>, GatewayError> {
618    let invoice = gateway
619        .handle_create_invoice_for_operator_msg(payload)
620        .await?;
621    Ok(Json(json!(invoice)))
622}
623
624#[instrument(target = LOG_GATEWAY, skip_all, err)]
625async fn pay_invoice_operator(
626    Extension(gateway): Extension<Arc<Gateway>>,
627    Json(payload): Json<PayInvoiceForOperatorPayload>,
628) -> Result<Json<serde_json::Value>, GatewayError> {
629    let preimage = gateway.handle_pay_invoice_for_operator_msg(payload).await?;
630    Ok(Json(json!(preimage.0.encode_hex::<String>())))
631}
632
633#[instrument(target = LOG_GATEWAY, skip_all, err)]
634async fn pay_invoice(
635    Extension(gateway): Extension<Arc<Gateway>>,
636    Json(payload): Json<fedimint_ln_client::pay::PayInvoicePayload>,
637) -> Result<Json<serde_json::Value>, GatewayError> {
638    let preimage = gateway.handle_pay_invoice_msg(payload).await?;
639    Ok(Json(json!(preimage.0.encode_hex::<String>())))
640}
641
642/// Connect a new federation
643#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
644async fn connect_fed(
645    Extension(gateway): Extension<Arc<Gateway>>,
646    Json(payload): Json<ConnectFedPayload>,
647) -> Result<Json<serde_json::Value>, GatewayError> {
648    let fed = gateway.handle_connect_federation(payload).await?;
649    Ok(Json(json!(fed)))
650}
651
652/// Leave a federation
653#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
654async fn leave_fed(
655    Extension(gateway): Extension<Arc<Gateway>>,
656    Json(payload): Json<LeaveFedPayload>,
657) -> Result<Json<serde_json::Value>, GatewayError> {
658    let fed = gateway.handle_leave_federation(payload).await?;
659    Ok(Json(json!(fed)))
660}
661
662/// Backup a gateway actor state
663#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
664async fn backup(
665    Extension(gateway): Extension<Arc<Gateway>>,
666    Json(payload): Json<BackupPayload>,
667) -> Result<Json<serde_json::Value>, GatewayError> {
668    gateway.handle_backup_msg(payload).await?;
669    Ok(Json(json!(())))
670}
671
672#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
673async fn set_fees(
674    Extension(gateway): Extension<Arc<Gateway>>,
675    Json(payload): Json<SetFeesPayload>,
676) -> Result<Json<serde_json::Value>, GatewayError> {
677    gateway.handle_set_fees_msg(payload).await?;
678    Ok(Json(json!(())))
679}
680
681#[instrument(target = LOG_GATEWAY, skip_all, err)]
682async fn get_ln_onchain_address(
683    Extension(gateway): Extension<Arc<Gateway>>,
684) -> Result<Json<serde_json::Value>, GatewayError> {
685    let address = gateway.handle_get_ln_onchain_address_msg().await?;
686    Ok(Json(json!(address.to_string())))
687}
688
689#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
690async fn open_channel(
691    Extension(gateway): Extension<Arc<Gateway>>,
692    Json(mut payload): Json<OpenChannelRequest>,
693) -> Result<Json<serde_json::Value>, GatewayError> {
694    payload.push_amount_sats = 0;
695    let funding_txid = gateway.handle_open_channel_msg(payload).await?;
696    Ok(Json(json!(funding_txid)))
697}
698
699#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
700async fn connect_peer(
701    Extension(gateway): Extension<Arc<Gateway>>,
702    Json(payload): Json<ConnectPeerRequest>,
703) -> Result<Json<serde_json::Value>, GatewayError> {
704    gateway.handle_connect_peer_msg(payload).await?;
705    Ok(Json(json!(())))
706}
707
708#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
709async fn open_channel_with_push(
710    Extension(gateway): Extension<Arc<Gateway>>,
711    Json(payload): Json<OpenChannelRequest>,
712) -> Result<Json<serde_json::Value>, GatewayError> {
713    let funding_txid = gateway.handle_open_channel_msg(payload).await?;
714    Ok(Json(json!(funding_txid)))
715}
716
717#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
718async fn close_channels_with_peer(
719    Extension(gateway): Extension<Arc<Gateway>>,
720    Json(payload): Json<CloseChannelsWithPeerRequest>,
721) -> Result<Json<serde_json::Value>, GatewayError> {
722    let response = gateway.handle_close_channels_with_peer_msg(payload).await?;
723    Ok(Json(json!(response)))
724}
725
726#[instrument(target = LOG_GATEWAY, skip_all, err)]
727async fn list_channels(
728    Extension(gateway): Extension<Arc<Gateway>>,
729) -> Result<Json<serde_json::Value>, GatewayError> {
730    let channels = gateway.handle_list_channels_msg().await?;
731    Ok(Json(json!(channels)))
732}
733
734#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
735async fn set_channel_fees(
736    Extension(gateway): Extension<Arc<Gateway>>,
737    Json(payload): Json<SetChannelFeesRequest>,
738) -> Result<Json<serde_json::Value>, GatewayError> {
739    gateway.handle_set_channel_fees_msg(payload).await?;
740    Ok(Json(json!(())))
741}
742
743#[instrument(target = LOG_GATEWAY, skip_all, err)]
744async fn send_onchain(
745    Extension(gateway): Extension<Arc<Gateway>>,
746    Json(payload): Json<SendOnchainRequest>,
747) -> Result<Json<serde_json::Value>, GatewayError> {
748    let txid = gateway.handle_send_onchain_msg(payload).await?;
749    Ok(Json(json!(txid)))
750}
751
752#[instrument(target = LOG_GATEWAY, skip_all, err)]
753async fn recheck_address(
754    Extension(gateway): Extension<Arc<Gateway>>,
755    Json(payload): Json<DepositAddressRecheckPayload>,
756) -> Result<Json<serde_json::Value>, GatewayError> {
757    gateway.handle_recheck_address_msg(payload).await?;
758    Ok(Json(json!({})))
759}
760
761#[instrument(target = LOG_GATEWAY, skip_all, err)]
762async fn get_balances(
763    Extension(gateway): Extension<Arc<Gateway>>,
764) -> Result<Json<serde_json::Value>, GatewayError> {
765    let balances = gateway.handle_get_balances_msg().await?;
766    Ok(Json(json!(balances)))
767}
768
769#[instrument(target = LOG_GATEWAY, skip_all, err)]
770async fn get_gateway_id(
771    Extension(gateway): Extension<Arc<Gateway>>,
772) -> Result<Json<serde_json::Value>, GatewayError> {
773    Ok(Json(json!(gateway.http_gateway_id().await)))
774}
775
776#[instrument(target = LOG_GATEWAY, skip_all, err)]
777async fn routing_info_v2(
778    Extension(gateway): Extension<Arc<Gateway>>,
779    Json(federation_id): Json<FederationId>,
780) -> Result<Json<serde_json::Value>, GatewayError> {
781    let routing_info = gateway.routing_info_v2(&federation_id).await?;
782    Ok(Json(json!(routing_info)))
783}
784
785#[instrument(target = LOG_GATEWAY, skip_all, err)]
786async fn pay_bolt11_invoice_v2(
787    Extension(gateway): Extension<Arc<Gateway>>,
788    Json(payload): Json<SendPaymentPayload>,
789) -> Result<Json<serde_json::Value>, GatewayError> {
790    let payment_result = gateway.send_payment_v2(payload).await?;
791    Ok(Json(json!(payment_result)))
792}
793
794#[instrument(target = LOG_GATEWAY, skip_all, err)]
795async fn create_bolt11_invoice_v2(
796    Extension(gateway): Extension<Arc<Gateway>>,
797    Json(payload): Json<CreateBolt11InvoicePayload>,
798) -> Result<Json<serde_json::Value>, GatewayError> {
799    let invoice = gateway.create_bolt11_invoice_v2(payload).await?;
800    Ok(Json(json!(invoice)))
801}
802
803pub(crate) async fn verify_bolt11_preimage_v2_get(
804    Extension(gateway): Extension<Arc<Gateway>>,
805    Path(payment_hash): Path<sha256::Hash>,
806    Query(query): Query<HashMap<String, String>>,
807) -> Result<Json<serde_json::Value>, GatewayError> {
808    let response = gateway
809        .verify_bolt11_preimage_v2(payment_hash, query.contains_key("wait"))
810        .await
811        .map_err(|e| LnurlError::internal(anyhow!(e)))?;
812
813    Ok(Json(json!(LnurlResponse::Ok(response))))
814}
815
816#[instrument(target = LOG_GATEWAY, skip_all, err)]
817async fn spend_ecash(
818    Extension(gateway): Extension<Arc<Gateway>>,
819    Json(payload): Json<SpendEcashPayload>,
820) -> Result<Json<serde_json::Value>, GatewayError> {
821    Ok(Json(json!(gateway.handle_spend_ecash_msg(payload).await?)))
822}
823
824#[instrument(target = LOG_GATEWAY, skip_all, err)]
825async fn receive_ecash(
826    Extension(gateway): Extension<Arc<Gateway>>,
827    Json(payload): Json<ReceiveEcashPayload>,
828) -> Result<Json<serde_json::Value>, GatewayError> {
829    Ok(Json(json!(
830        gateway.handle_receive_ecash_msg(payload).await?
831    )))
832}
833
834#[instrument(target = LOG_GATEWAY, skip_all, err)]
835async fn mnemonic(
836    Extension(gateway): Extension<Arc<Gateway>>,
837) -> Result<Json<serde_json::Value>, GatewayError> {
838    let words = gateway.handle_mnemonic_msg().await?;
839    Ok(Json(json!(words)))
840}
841
842#[instrument(target = LOG_GATEWAY, skip_all, err)]
843async fn set_mnemonic(
844    Extension(gateway): Extension<Arc<Gateway>>,
845    Json(payload): Json<SetMnemonicPayload>,
846) -> Result<Json<serde_json::Value>, GatewayError> {
847    gateway.handle_set_mnemonic_msg(payload).await?;
848    Ok(Json(json!(())))
849}
850
851#[instrument(target = LOG_GATEWAY, skip_all, err)]
852pub(crate) async fn stop(
853    Extension(task_group): Extension<TaskGroup>,
854    Extension(gateway): Extension<Arc<Gateway>>,
855) -> Result<Json<serde_json::Value>, GatewayError> {
856    gateway.handle_shutdown_msg(task_group).await?;
857    Ok(Json(json!(())))
858}
859
860/// `POST /payment_log` — returns a paginated list of gateway payment events.
861///
862/// If `event_kinds` is empty, only gateway payment-related events
863/// ([`ALL_GATEWAY_EVENTS`](crate::events::ALL_GATEWAY_EVENTS)) are returned.
864/// This means returned event IDs may be non-contiguous because other internal
865/// events share the same ID space but are excluded by default.
866#[instrument(target = LOG_GATEWAY, skip_all, err)]
867async fn payment_log(
868    Extension(gateway): Extension<Arc<Gateway>>,
869    Json(payload): Json<PaymentLogPayload>,
870) -> Result<Json<serde_json::Value>, GatewayError> {
871    let payment_log = gateway.handle_payment_log_msg(payload).await?;
872    Ok(Json(json!(payment_log)))
873}
874
875#[instrument(target = LOG_GATEWAY, skip_all, err)]
876async fn payment_summary(
877    Extension(gateway): Extension<Arc<Gateway>>,
878    Json(payload): Json<PaymentSummaryPayload>,
879) -> Result<Json<serde_json::Value>, GatewayError> {
880    let payment_summary = gateway.handle_payment_summary_msg(payload).await?;
881    Ok(Json(json!(payment_summary)))
882}
883
884#[instrument(target = LOG_GATEWAY, skip_all, err)]
885async fn get_invoice(
886    Extension(gateway): Extension<Arc<Gateway>>,
887    Json(payload): Json<GetInvoiceRequest>,
888) -> Result<Json<serde_json::Value>, GatewayError> {
889    let invoice = gateway.handle_get_invoice_msg(payload).await?;
890    Ok(Json(json!(invoice)))
891}
892
893#[instrument(target = LOG_GATEWAY, skip_all, err)]
894async fn list_transactions(
895    Extension(gateway): Extension<Arc<Gateway>>,
896    Json(payload): Json<ListTransactionsPayload>,
897) -> Result<Json<serde_json::Value>, GatewayError> {
898    let transactions = gateway.handle_list_transactions_msg(payload).await?;
899    Ok(Json(json!(transactions)))
900}
901
902#[instrument(target = LOG_GATEWAY, skip_all, err)]
903async fn create_offer_for_operator(
904    Extension(gateway): Extension<Arc<Gateway>>,
905    Json(payload): Json<CreateOfferPayload>,
906) -> Result<Json<serde_json::Value>, GatewayError> {
907    let offer = gateway
908        .handle_create_offer_for_operator_msg(payload)
909        .await?;
910    Ok(Json(json!(offer)))
911}
912
913#[instrument(target = LOG_GATEWAY, skip_all, err)]
914async fn pay_offer_operator(
915    Extension(gateway): Extension<Arc<Gateway>>,
916    Json(payload): Json<PayOfferPayload>,
917) -> Result<Json<serde_json::Value>, GatewayError> {
918    let response = gateway.handle_pay_offer_for_operator_msg(payload).await?;
919    Ok(Json(json!(response)))
920}
921
922#[instrument(target = LOG_GATEWAY, skip_all, err)]
923async fn invite_codes(
924    Extension(gateway): Extension<Arc<Gateway>>,
925) -> Result<Json<serde_json::Value>, GatewayError> {
926    let invite_codes = gateway.handle_export_invite_codes().await;
927    Ok(Json(json!(invite_codes)))
928}
929
930#[cfg(test)]
931mod tests {
932    use super::*;
933
934    #[test]
935    fn strip_v1_prefix_covers_both_api_mounts() {
936        for route in LIQUIDITY_MANAGER_ROUTES {
937            assert_eq!(strip_v1_prefix(route), route);
938            assert_eq!(
939                strip_v1_prefix(&format!("/{V1_API_ENDPOINT}{route}")),
940                route
941            );
942        }
943    }
944
945    #[test]
946    fn strip_v1_prefix_only_strips_a_full_leading_segment() {
947        assert_eq!(strip_v1_prefix("/v1"), "/v1");
948        assert_eq!(strip_v1_prefix("/v1x/address"), "/v1x/address");
949        assert_eq!(strip_v1_prefix("/v1/v1/address"), "/v1/address");
950    }
951}