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 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
43/// Creates the webserver's routes and spawns the webserver in a separate task.
44pub 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        // 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    // These routes are not available unless the gateway's configuration is set.
95    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
104/// Middleware to authenticate an incoming request. Routes that are
105/// authenticated with this middleware are un-authenticated if the gateway has
106/// not yet been configured. After the gateway is configured, this middleware
107/// enforces that a Bearer token must be supplied in the Authorization header.
108async fn auth_after_config_middleware(
109    Extension(gateway): Extension<Arc<Gateway>>,
110    request: Request,
111    next: Next,
112) -> Result<impl IntoResponse, StatusCode> {
113    // If the gateway's config has not been set, allow the request to continue, so
114    // that the gateway can be configured
115    let gateway_config = gateway.clone_gateway_config().await;
116    if gateway_config.is_none() {
117        return Ok(next.run(request).await);
118    }
119
120    // Otherwise, validate that the Bearer token matches the gateway's hashed
121    // password
122    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
128/// Validate that the Bearer token matches the gateway's hashed password
129async 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
144/// Public routes that are used in the LNv1 protocol
145fn 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
151/// Public routes that are used in the LNv2 protocol
152fn 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
162/// Gateway Webserver Routes. The gateway supports three types of routes
163/// - Always Authenticated: these routes always require a Bearer token. Used by
164///   gateway administrators.
165/// - Authenticated after config: these routes are unauthenticated before
166///   configuring the gateway to allow the user to set a password. After setting
167///   the password, they become authenticated.
168/// - Un-authenticated: anyone can request these routes. Used by fedimint
169///   clients.
170fn v1_routes(gateway: Arc<Gateway>, task_group: TaskGroup) -> Router {
171    // Public routes on gateway webserver
172    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    // Authenticated, public routes used for gateway administration
183    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    // Routes that are un-authenticated before gateway configuration, then become
212    // authenticated after a password has been set.
213    let authenticated_after_config_routes = Router::new()
214        .route(SET_CONFIGURATION_ENDPOINT, post(set_configuration))
215        .route(CONFIGURATION_ENDPOINT, post(configuration))
216        // FIXME: deprecated >= 0.3.0
217        .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
230/// Creates a password hash by appending a 4 byte salt to the plaintext
231/// password.
232pub 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/// Display high-level information about the Gateway
243// FIXME: deprecated >= 0.3.0
244// This endpoint exists only to remain backwards-compatible with the original POST endpoint
245#[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/// Display high-level information about the Gateway
255#[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/// Display high-level information about the Gateway config
264#[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/// Generate deposit address
276#[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/// Withdraw from a gateway federation.
286#[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/// Connect a new federation
325#[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/// Leave a federation
335#[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/// Backup a gateway actor state
345#[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}