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, FEDERATION_STATUS_ENDPOINT,
22    FederationStatusRequest, GATEWAY_INFO_ENDPOINT, GET_BALANCES_ENDPOINT, GET_INVOICE_ENDPOINT,
23    GET_LN_ONCHAIN_ADDRESS_ENDPOINT, GetInvoiceRequest, INVITE_CODES_ENDPOINT, LEAVE_FED_ENDPOINT,
24    LIST_CHANNELS_ENDPOINT, LIST_TRANSACTIONS_ENDPOINT, LeaveFedPayload, ListTransactionsPayload,
25    MNEMONIC_ENDPOINT, 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, PublicGatewayError};
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        let is_setup_route = fedimint_gateway_ui::is_allowed_setup_route(path);
174
175        if !is_allowed_not_configured_api(&method, path) && !is_setup_route {
176            return Err(StatusCode::NOT_FOUND);
177        }
178    }
179
180    Ok(next.run(request).await)
181}
182
183pub(crate) fn is_allowed_not_configured_api(method: &axum::http::Method, path: &str) -> bool {
184    method == axum::http::Method::POST && strip_v1_prefix(path) == MNEMONIC_ENDPOINT
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
295fn public_routes(handlers: &mut Handlers) -> Router {
296    let mut routes = register_post_handler(
297        handlers,
298        RECEIVE_ECASH_ENDPOINT,
299        receive_ecash,
300        false,
301        Router::new(),
302    );
303    routes = routes.merge(lnv1_routes(handlers));
304    routes = routes.merge(lnv2_routes(handlers));
305    register_post_handler(
306        handlers,
307        FEDERATION_STATUS_ENDPOINT,
308        federation_status,
309        false,
310        routes,
311    )
312}
313
314/// Gateway Webserver Routes. The gateway supports two types of routes
315/// - Always Authenticated: these routes always require a Bearer token. Used by
316///   gateway administrators.
317/// - Un-authenticated: anyone can request these routes. Used by fedimint
318///   clients.
319fn routes(gateway: Arc<Gateway>, task_group: TaskGroup, handlers: &mut Handlers) -> Router {
320    // Public routes on gateway webserver
321    let public_routes = public_routes(handlers);
322
323    // Authenticated routes used for gateway administration
324    let is_authenticated = true;
325    let authenticated_routes = Router::new();
326    let authenticated_routes = register_post_handler(
327        handlers,
328        ADDRESS_ENDPOINT,
329        address,
330        is_authenticated,
331        authenticated_routes,
332    );
333    let authenticated_routes = register_post_handler(
334        handlers,
335        WITHDRAW_ENDPOINT,
336        withdraw,
337        is_authenticated,
338        authenticated_routes,
339    );
340    let authenticated_routes = register_post_handler(
341        handlers,
342        WITHDRAW_TO_ONCHAIN_ENDPOINT,
343        withdraw_to_onchain,
344        is_authenticated,
345        authenticated_routes,
346    );
347    let authenticated_routes = register_post_handler(
348        handlers,
349        PEGIN_FROM_ONCHAIN_ENDPOINT,
350        pegin_from_onchain,
351        is_authenticated,
352        authenticated_routes,
353    );
354    let authenticated_routes = register_post_handler(
355        handlers,
356        CONNECT_FED_ENDPOINT,
357        connect_fed,
358        is_authenticated,
359        authenticated_routes,
360    );
361    let authenticated_routes = register_post_handler(
362        handlers,
363        LEAVE_FED_ENDPOINT,
364        leave_fed,
365        is_authenticated,
366        authenticated_routes,
367    );
368    let authenticated_routes = register_post_handler(
369        handlers,
370        BACKUP_ENDPOINT,
371        backup,
372        is_authenticated,
373        authenticated_routes,
374    );
375    let authenticated_routes = register_post_handler(
376        handlers,
377        CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT,
378        create_invoice_for_operator,
379        is_authenticated,
380        authenticated_routes,
381    );
382    let authenticated_routes = register_post_handler(
383        handlers,
384        CREATE_BOLT12_OFFER_FOR_OPERATOR_ENDPOINT,
385        create_offer_for_operator,
386        is_authenticated,
387        authenticated_routes,
388    );
389    let authenticated_routes = register_post_handler(
390        handlers,
391        PAY_INVOICE_FOR_OPERATOR_ENDPOINT,
392        pay_invoice_operator,
393        is_authenticated,
394        authenticated_routes,
395    );
396    let authenticated_routes = register_post_handler(
397        handlers,
398        PAY_OFFER_FOR_OPERATOR_ENDPOINT,
399        pay_offer_operator,
400        is_authenticated,
401        authenticated_routes,
402    );
403    let authenticated_routes = register_post_handler(
404        handlers,
405        GET_INVOICE_ENDPOINT,
406        get_invoice,
407        is_authenticated,
408        authenticated_routes,
409    );
410    let authenticated_routes = register_get_handler(
411        handlers,
412        GET_LN_ONCHAIN_ADDRESS_ENDPOINT,
413        get_ln_onchain_address,
414        is_authenticated,
415        authenticated_routes,
416    );
417    let authenticated_routes = register_post_handler(
418        handlers,
419        OPEN_CHANNEL_ENDPOINT,
420        open_channel,
421        is_authenticated,
422        authenticated_routes,
423    );
424    let authenticated_routes = register_post_handler(
425        handlers,
426        CONNECT_PEER_ENDPOINT,
427        connect_peer,
428        is_authenticated,
429        authenticated_routes,
430    );
431    let authenticated_routes = register_post_handler(
432        handlers,
433        OPEN_CHANNEL_WITH_PUSH_ENDPOINT,
434        open_channel_with_push,
435        is_authenticated,
436        authenticated_routes,
437    );
438    let authenticated_routes = register_post_handler(
439        handlers,
440        CLOSE_CHANNELS_WITH_PEER_ENDPOINT,
441        close_channels_with_peer,
442        is_authenticated,
443        authenticated_routes,
444    );
445    let authenticated_routes = register_get_handler(
446        handlers,
447        LIST_CHANNELS_ENDPOINT,
448        list_channels,
449        is_authenticated,
450        authenticated_routes,
451    );
452    let authenticated_routes = register_post_handler(
453        handlers,
454        SET_CHANNEL_FEES_ENDPOINT,
455        set_channel_fees,
456        is_authenticated,
457        authenticated_routes,
458    );
459    let authenticated_routes = register_post_handler(
460        handlers,
461        LIST_TRANSACTIONS_ENDPOINT,
462        list_transactions,
463        is_authenticated,
464        authenticated_routes,
465    );
466    let authenticated_routes = register_post_handler(
467        handlers,
468        SEND_ONCHAIN_ENDPOINT,
469        send_onchain,
470        is_authenticated,
471        authenticated_routes,
472    );
473    let authenticated_routes = register_post_handler(
474        handlers,
475        ADDRESS_RECHECK_ENDPOINT,
476        recheck_address,
477        is_authenticated,
478        authenticated_routes,
479    );
480    let authenticated_routes = register_get_handler(
481        handlers,
482        GET_BALANCES_ENDPOINT,
483        get_balances,
484        is_authenticated,
485        authenticated_routes,
486    );
487    let authenticated_routes = register_post_handler(
488        handlers,
489        SPEND_ECASH_ENDPOINT,
490        spend_ecash,
491        is_authenticated,
492        authenticated_routes,
493    );
494    let authenticated_routes = register_get_handler(
495        handlers,
496        MNEMONIC_ENDPOINT,
497        mnemonic,
498        is_authenticated,
499        authenticated_routes,
500    );
501    // Stop does not have the same function signature, it is handled separately
502    let authenticated_routes = authenticated_routes.route(STOP_ENDPOINT, get(stop));
503    let authenticated_routes = register_post_handler(
504        handlers,
505        PAYMENT_LOG_ENDPOINT,
506        payment_log,
507        is_authenticated,
508        authenticated_routes,
509    );
510    let authenticated_routes = register_post_handler(
511        handlers,
512        PAYMENT_SUMMARY_ENDPOINT,
513        payment_summary,
514        is_authenticated,
515        authenticated_routes,
516    );
517    let authenticated_routes = register_post_handler(
518        handlers,
519        SET_FEES_ENDPOINT,
520        set_fees,
521        is_authenticated,
522        authenticated_routes,
523    );
524    let authenticated_routes = register_post_handler(
525        handlers,
526        CONFIGURATION_ENDPOINT,
527        configuration,
528        is_authenticated,
529        authenticated_routes,
530    );
531    let authenticated_routes = register_get_handler(
532        handlers,
533        GATEWAY_INFO_ENDPOINT,
534        info,
535        is_authenticated,
536        authenticated_routes,
537    );
538    let authenticated_routes = register_post_handler(
539        handlers,
540        MNEMONIC_ENDPOINT,
541        set_mnemonic,
542        is_authenticated,
543        authenticated_routes,
544    );
545    let authenticated_routes = register_get_handler(
546        handlers,
547        INVITE_CODES_ENDPOINT,
548        invite_codes,
549        is_authenticated,
550        authenticated_routes,
551    );
552    let authenticated_routes = authenticated_routes.layer(middleware::from_fn(auth_middleware));
553
554    Router::new()
555        .merge(public_routes)
556        .merge(authenticated_routes)
557        .layer(middleware::from_fn(not_configured_middleware))
558        .layer(Extension(gateway))
559        .layer(Extension(task_group))
560        .layer(CorsLayer::permissive())
561}
562
563/// Display high-level information about the Gateway
564#[instrument(target = LOG_GATEWAY, skip_all, err)]
565async fn info(
566    Extension(gateway): Extension<Arc<Gateway>>,
567) -> Result<Json<serde_json::Value>, GatewayError> {
568    let info = gateway.handle_get_info().await?;
569    Ok(Json(json!(info)))
570}
571
572/// Reports sanitized capability and health for exactly one requested
573/// federation.
574#[instrument(target = LOG_GATEWAY, skip_all, fields(federation_id = %payload.federation_id))]
575async fn federation_status(
576    Extension(gateway): Extension<Arc<Gateway>>,
577    Json(payload): Json<FederationStatusRequest>,
578) -> Result<Json<serde_json::Value>, GatewayError> {
579    let status = gateway
580        .handle_federation_status(payload.federation_id)
581        .await
582        .map_err(PublicGatewayError::Internal)?;
583    Ok(Json(json!(status)))
584}
585
586/// Display high-level information about the Gateway config
587#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
588async fn configuration(
589    Extension(gateway): Extension<Arc<Gateway>>,
590    Json(payload): Json<ConfigPayload>,
591) -> Result<Json<serde_json::Value>, GatewayError> {
592    let gateway_fed_config = gateway
593        .handle_get_federation_config(payload.federation_id)
594        .await?;
595    Ok(Json(json!(gateway_fed_config)))
596}
597
598/// Generate deposit address
599#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
600async fn address(
601    Extension(gateway): Extension<Arc<Gateway>>,
602    Json(payload): Json<DepositAddressPayload>,
603) -> Result<Json<serde_json::Value>, GatewayError> {
604    let address = gateway.handle_address_msg(payload).await?;
605    Ok(Json(json!(address)))
606}
607
608/// Pegs in funds from the gateway's onchain wallet
609#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
610async fn pegin_from_onchain(
611    Extension(gateway): Extension<Arc<Gateway>>,
612    Json(payload): Json<PeginFromOnchainPayload>,
613) -> Result<Json<serde_json::Value>, GatewayError> {
614    let address = gateway.handle_pegin_from_onchain_msg(payload).await?;
615    Ok(Json(json!(address)))
616}
617
618/// Withdraw from a gateway federation.
619#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
620async fn withdraw(
621    Extension(gateway): Extension<Arc<Gateway>>,
622    Json(payload): Json<WithdrawPayload>,
623) -> Result<Json<serde_json::Value>, GatewayError> {
624    let txid = gateway.handle_withdraw_msg(payload).await?;
625    Ok(Json(json!(txid)))
626}
627
628/// Withdraw from a gateway federation.
629#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
630async fn withdraw_to_onchain(
631    Extension(gateway): Extension<Arc<Gateway>>,
632    Json(payload): Json<WithdrawToOnchainPayload>,
633) -> Result<Json<serde_json::Value>, GatewayError> {
634    let txid = gateway.handle_withdraw_to_onchain_msg(payload).await?;
635    Ok(Json(json!(txid)))
636}
637
638#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
639async fn create_invoice_for_operator(
640    Extension(gateway): Extension<Arc<Gateway>>,
641    Json(payload): Json<CreateInvoiceForOperatorPayload>,
642) -> Result<Json<serde_json::Value>, GatewayError> {
643    let invoice = gateway
644        .handle_create_invoice_for_operator_msg(payload)
645        .await?;
646    Ok(Json(json!(invoice)))
647}
648
649#[instrument(target = LOG_GATEWAY, skip_all, err)]
650async fn pay_invoice_operator(
651    Extension(gateway): Extension<Arc<Gateway>>,
652    Json(payload): Json<PayInvoiceForOperatorPayload>,
653) -> Result<Json<serde_json::Value>, GatewayError> {
654    let preimage = gateway.handle_pay_invoice_for_operator_msg(payload).await?;
655    Ok(Json(json!(preimage.0.encode_hex::<String>())))
656}
657
658#[instrument(target = LOG_GATEWAY, skip_all, err)]
659async fn pay_invoice(
660    Extension(gateway): Extension<Arc<Gateway>>,
661    Json(payload): Json<fedimint_ln_client::pay::PayInvoicePayload>,
662) -> Result<Json<serde_json::Value>, GatewayError> {
663    let preimage = gateway.handle_pay_invoice_msg(payload).await?;
664    Ok(Json(json!(preimage.0.encode_hex::<String>())))
665}
666
667/// Connect a new federation
668#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
669async fn connect_fed(
670    Extension(gateway): Extension<Arc<Gateway>>,
671    Json(payload): Json<ConnectFedPayload>,
672) -> Result<Json<serde_json::Value>, GatewayError> {
673    let fed = gateway.handle_connect_federation(payload).await?;
674    Ok(Json(json!(fed)))
675}
676
677/// Leave a federation
678#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
679async fn leave_fed(
680    Extension(gateway): Extension<Arc<Gateway>>,
681    Json(payload): Json<LeaveFedPayload>,
682) -> Result<Json<serde_json::Value>, GatewayError> {
683    let fed = gateway.handle_leave_federation(payload).await?;
684    Ok(Json(json!(fed)))
685}
686
687/// Backup a gateway actor state
688#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
689async fn backup(
690    Extension(gateway): Extension<Arc<Gateway>>,
691    Json(payload): Json<BackupPayload>,
692) -> Result<Json<serde_json::Value>, GatewayError> {
693    gateway.handle_backup_msg(payload).await?;
694    Ok(Json(json!(())))
695}
696
697#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
698async fn set_fees(
699    Extension(gateway): Extension<Arc<Gateway>>,
700    Json(payload): Json<SetFeesPayload>,
701) -> Result<Json<serde_json::Value>, GatewayError> {
702    gateway.handle_set_fees_msg(payload).await?;
703    Ok(Json(json!(())))
704}
705
706#[instrument(target = LOG_GATEWAY, skip_all, err)]
707async fn get_ln_onchain_address(
708    Extension(gateway): Extension<Arc<Gateway>>,
709) -> Result<Json<serde_json::Value>, GatewayError> {
710    let address = gateway.handle_get_ln_onchain_address_msg().await?;
711    Ok(Json(json!(address.to_string())))
712}
713
714#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
715async fn open_channel(
716    Extension(gateway): Extension<Arc<Gateway>>,
717    Json(mut payload): Json<OpenChannelRequest>,
718) -> Result<Json<serde_json::Value>, GatewayError> {
719    payload.push_amount_sats = 0;
720    let funding_txid = gateway.handle_open_channel_msg(payload).await?;
721    Ok(Json(json!(funding_txid)))
722}
723
724#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
725async fn connect_peer(
726    Extension(gateway): Extension<Arc<Gateway>>,
727    Json(payload): Json<ConnectPeerRequest>,
728) -> Result<Json<serde_json::Value>, GatewayError> {
729    gateway.handle_connect_peer_msg(payload).await?;
730    Ok(Json(json!(())))
731}
732
733#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
734async fn open_channel_with_push(
735    Extension(gateway): Extension<Arc<Gateway>>,
736    Json(payload): Json<OpenChannelRequest>,
737) -> Result<Json<serde_json::Value>, GatewayError> {
738    let funding_txid = gateway.handle_open_channel_msg(payload).await?;
739    Ok(Json(json!(funding_txid)))
740}
741
742#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
743async fn close_channels_with_peer(
744    Extension(gateway): Extension<Arc<Gateway>>,
745    Json(payload): Json<CloseChannelsWithPeerRequest>,
746) -> Result<Json<serde_json::Value>, GatewayError> {
747    let response = gateway.handle_close_channels_with_peer_msg(payload).await?;
748    Ok(Json(json!(response)))
749}
750
751#[instrument(target = LOG_GATEWAY, skip_all, err)]
752async fn list_channels(
753    Extension(gateway): Extension<Arc<Gateway>>,
754) -> Result<Json<serde_json::Value>, GatewayError> {
755    let channels = gateway.handle_list_channels_msg().await?;
756    Ok(Json(json!(channels)))
757}
758
759#[instrument(target = LOG_GATEWAY, skip_all, err, fields(?payload))]
760async fn set_channel_fees(
761    Extension(gateway): Extension<Arc<Gateway>>,
762    Json(payload): Json<SetChannelFeesRequest>,
763) -> Result<Json<serde_json::Value>, GatewayError> {
764    gateway.handle_set_channel_fees_msg(payload).await?;
765    Ok(Json(json!(())))
766}
767
768#[instrument(target = LOG_GATEWAY, skip_all, err)]
769async fn send_onchain(
770    Extension(gateway): Extension<Arc<Gateway>>,
771    Json(payload): Json<SendOnchainRequest>,
772) -> Result<Json<serde_json::Value>, GatewayError> {
773    let txid = gateway.handle_send_onchain_msg(payload).await?;
774    Ok(Json(json!(txid)))
775}
776
777#[instrument(target = LOG_GATEWAY, skip_all, err)]
778async fn recheck_address(
779    Extension(gateway): Extension<Arc<Gateway>>,
780    Json(payload): Json<DepositAddressRecheckPayload>,
781) -> Result<Json<serde_json::Value>, GatewayError> {
782    gateway.handle_recheck_address_msg(payload).await?;
783    Ok(Json(json!({})))
784}
785
786#[instrument(target = LOG_GATEWAY, skip_all, err)]
787async fn get_balances(
788    Extension(gateway): Extension<Arc<Gateway>>,
789) -> Result<Json<serde_json::Value>, GatewayError> {
790    let balances = gateway.handle_get_balances_msg().await?;
791    Ok(Json(json!(balances)))
792}
793
794#[instrument(target = LOG_GATEWAY, skip_all, err)]
795async fn get_gateway_id(
796    Extension(gateway): Extension<Arc<Gateway>>,
797) -> Result<Json<serde_json::Value>, GatewayError> {
798    Ok(Json(json!(gateway.http_gateway_id().await)))
799}
800
801#[instrument(target = LOG_GATEWAY, skip_all, err)]
802async fn routing_info_v2(
803    Extension(gateway): Extension<Arc<Gateway>>,
804    Json(federation_id): Json<FederationId>,
805) -> Result<Json<serde_json::Value>, GatewayError> {
806    let routing_info = gateway.routing_info_v2(&federation_id).await?;
807    Ok(Json(json!(routing_info)))
808}
809
810#[instrument(target = LOG_GATEWAY, skip_all, err)]
811async fn pay_bolt11_invoice_v2(
812    Extension(gateway): Extension<Arc<Gateway>>,
813    Json(payload): Json<SendPaymentPayload>,
814) -> Result<Json<serde_json::Value>, GatewayError> {
815    let payment_result = gateway.send_payment_v2(payload).await?;
816    Ok(Json(json!(payment_result)))
817}
818
819#[instrument(target = LOG_GATEWAY, skip_all, err)]
820async fn create_bolt11_invoice_v2(
821    Extension(gateway): Extension<Arc<Gateway>>,
822    Json(payload): Json<CreateBolt11InvoicePayload>,
823) -> Result<Json<serde_json::Value>, GatewayError> {
824    let invoice = gateway.create_bolt11_invoice_v2(payload).await?;
825    Ok(Json(json!(invoice)))
826}
827
828pub(crate) async fn verify_bolt11_preimage_v2_get(
829    Extension(gateway): Extension<Arc<Gateway>>,
830    Path(payment_hash): Path<sha256::Hash>,
831    Query(query): Query<HashMap<String, String>>,
832) -> Result<Json<serde_json::Value>, GatewayError> {
833    let response = gateway
834        .verify_bolt11_preimage_v2(payment_hash, query.contains_key("wait"))
835        .await
836        .map_err(|e| LnurlError::internal(anyhow!(e)))?;
837
838    Ok(Json(json!(LnurlResponse::Ok(response))))
839}
840
841#[instrument(target = LOG_GATEWAY, skip_all, err)]
842async fn spend_ecash(
843    Extension(gateway): Extension<Arc<Gateway>>,
844    Json(payload): Json<SpendEcashPayload>,
845) -> Result<Json<serde_json::Value>, GatewayError> {
846    Ok(Json(json!(gateway.handle_spend_ecash_msg(payload).await?)))
847}
848
849#[instrument(target = LOG_GATEWAY, skip_all, err)]
850async fn receive_ecash(
851    Extension(gateway): Extension<Arc<Gateway>>,
852    Json(payload): Json<ReceiveEcashPayload>,
853) -> Result<Json<serde_json::Value>, GatewayError> {
854    Ok(Json(json!(
855        gateway.handle_receive_ecash_msg(payload).await?
856    )))
857}
858
859#[instrument(target = LOG_GATEWAY, skip_all, err)]
860async fn mnemonic(
861    Extension(gateway): Extension<Arc<Gateway>>,
862) -> Result<Json<serde_json::Value>, GatewayError> {
863    let words = gateway.handle_mnemonic_msg().await?;
864    Ok(Json(json!(words)))
865}
866
867#[instrument(target = LOG_GATEWAY, skip_all, err)]
868async fn set_mnemonic(
869    Extension(gateway): Extension<Arc<Gateway>>,
870    Json(payload): Json<SetMnemonicPayload>,
871) -> Result<Json<serde_json::Value>, GatewayError> {
872    gateway.handle_set_mnemonic_msg(payload).await?;
873    Ok(Json(json!(())))
874}
875
876#[instrument(target = LOG_GATEWAY, skip_all, err)]
877pub(crate) async fn stop(
878    Extension(task_group): Extension<TaskGroup>,
879    Extension(gateway): Extension<Arc<Gateway>>,
880) -> Result<Json<serde_json::Value>, GatewayError> {
881    gateway.handle_shutdown_msg(task_group).await?;
882    Ok(Json(json!(())))
883}
884
885/// `POST /payment_log` — returns a paginated list of gateway payment events.
886///
887/// If `event_kinds` is empty, only gateway payment-related events
888/// ([`ALL_GATEWAY_EVENTS`](crate::events::ALL_GATEWAY_EVENTS)) are returned.
889/// This means returned event IDs may be non-contiguous because other internal
890/// events share the same ID space but are excluded by default.
891#[instrument(target = LOG_GATEWAY, skip_all, err)]
892async fn payment_log(
893    Extension(gateway): Extension<Arc<Gateway>>,
894    Json(payload): Json<PaymentLogPayload>,
895) -> Result<Json<serde_json::Value>, GatewayError> {
896    let payment_log = gateway.handle_payment_log_msg(payload).await?;
897    Ok(Json(json!(payment_log)))
898}
899
900#[instrument(target = LOG_GATEWAY, skip_all, err)]
901async fn payment_summary(
902    Extension(gateway): Extension<Arc<Gateway>>,
903    Json(payload): Json<PaymentSummaryPayload>,
904) -> Result<Json<serde_json::Value>, GatewayError> {
905    let payment_summary = gateway.handle_payment_summary_msg(payload).await?;
906    Ok(Json(json!(payment_summary)))
907}
908
909#[instrument(target = LOG_GATEWAY, skip_all, err)]
910async fn get_invoice(
911    Extension(gateway): Extension<Arc<Gateway>>,
912    Json(payload): Json<GetInvoiceRequest>,
913) -> Result<Json<serde_json::Value>, GatewayError> {
914    let invoice = gateway.handle_get_invoice_msg(payload).await?;
915    Ok(Json(json!(invoice)))
916}
917
918#[instrument(target = LOG_GATEWAY, skip_all, err)]
919async fn list_transactions(
920    Extension(gateway): Extension<Arc<Gateway>>,
921    Json(payload): Json<ListTransactionsPayload>,
922) -> Result<Json<serde_json::Value>, GatewayError> {
923    let transactions = gateway.handle_list_transactions_msg(payload).await?;
924    Ok(Json(json!(transactions)))
925}
926
927#[instrument(target = LOG_GATEWAY, skip_all, err)]
928async fn create_offer_for_operator(
929    Extension(gateway): Extension<Arc<Gateway>>,
930    Json(payload): Json<CreateOfferPayload>,
931) -> Result<Json<serde_json::Value>, GatewayError> {
932    let offer = gateway
933        .handle_create_offer_for_operator_msg(payload)
934        .await?;
935    Ok(Json(json!(offer)))
936}
937
938#[instrument(target = LOG_GATEWAY, skip_all, err)]
939async fn pay_offer_operator(
940    Extension(gateway): Extension<Arc<Gateway>>,
941    Json(payload): Json<PayOfferPayload>,
942) -> Result<Json<serde_json::Value>, GatewayError> {
943    let response = gateway.handle_pay_offer_for_operator_msg(payload).await?;
944    Ok(Json(json!(response)))
945}
946
947#[instrument(target = LOG_GATEWAY, skip_all, err)]
948async fn invite_codes(
949    Extension(gateway): Extension<Arc<Gateway>>,
950) -> Result<Json<serde_json::Value>, GatewayError> {
951    let invite_codes = gateway.handle_export_invite_codes().await;
952    Ok(Json(json!(invite_codes)))
953}
954
955#[cfg(test)]
956mod tests;