ln_gateway/rpc/
rpc_server.rs

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
42/// Creates the webserver's routes and spawns the webserver in a separate task.
43pub 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        // Backwards compatibility: Continue supporting gateway APIs without versioning
49        .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
71/// Extracts the Bearer token from the Authorization header of the request.
72fn 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
86/// Middleware to authenticate an incoming request. Routes that are
87/// authenticated with this middleware always require a Bearer token to be
88/// supplied in the Authorization header.
89async 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
104/// Public routes that are used in the LNv1 protocol
105fn 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
111/// Public routes that are used in the LNv2 protocol
112fn 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
122/// Gateway Webserver Routes. The gateway supports three types of routes
123/// - Always Authenticated: these routes always require a Bearer token. Used by
124///   gateway administrators.
125/// - Authenticated after config: these routes are unauthenticated before
126///   configuring the gateway to allow the user to set a password. After setting
127///   the password, they become authenticated.
128/// - Un-authenticated: anyone can request these routes. Used by fedimint
129///   clients.
130fn v1_routes(gateway: Arc<Gateway>, task_group: TaskGroup) -> Router {
131    // Public routes on gateway webserver
132    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    // Authenticated routes used for gateway administration
143    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        // FIXME: deprecated >= 0.3.0
174        .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/// Display high-level information about the Gateway
187// FIXME: deprecated >= 0.3.0
188// This endpoint exists only to remain backwards-compatible with the original POST endpoint
189#[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/// Display high-level information about the Gateway
199#[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/// Display high-level information about the Gateway config
208#[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/// Generate deposit address
220#[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/// Withdraw from a gateway federation.
230#[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/// Connect a new federation
269#[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/// Leave a federation
279#[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/// Backup a gateway actor state
289#[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}