1use std::{
10 borrow::Cow,
11 sync::Arc,
12 time::{Duration, SystemTime},
13};
14
15use anyhow::{Context, ensure};
16use arc_swap::ArcSwapOption;
17use async_trait::async_trait;
18use lexe_api::{
19 auth::{self, BearerAuthenticator},
20 def::{
21 AppBackendApi, AppGatewayApi, AppNodeProvisionApi, AppNodeRunApi,
22 BearerAuthBackendApi,
23 },
24 error::{BackendApiError, GatewayApiError, NodeApiError, NodeErrorKind},
25 models::{
26 command::{
27 BackupInfo, CloseChannelRequest, CreateInvoiceRequest,
28 CreateInvoiceResponse, CreateOfferRequest, CreateOfferResponse,
29 DebugInfo, EnclavesToProvisionRequest, GetAddressResponse,
30 GetHumanBitcoinAddressResponse, GetNewPayments, GetUpdatedPayments,
31 HumanBitcoinAddressV1, ListChannelsResponse, NodeInfo, NodeInfoV1,
32 OpenChannelRequest, OpenChannelResponse, PayInvoiceRequest,
33 PayInvoiceResponse, PayOfferRequest, PayOfferResponse,
34 PayOnchainRequest, PayOnchainResponse, PaymentCreatedIndexes,
35 PaymentIdStruct, PreflightCloseChannelRequest,
36 PreflightCloseChannelResponse, PreflightOpenChannelRequest,
37 PreflightOpenChannelResponse, PreflightPayInvoiceRequest,
38 PreflightPayInvoiceResponse, PreflightPayOfferRequest,
39 PreflightPayOfferResponse, PreflightPayOnchainRequest,
40 PreflightPayOnchainResponse, SetupGDrive, UpdatePersonalNote,
41 UpsertHumanBitcoinAddressResponse,
42 },
43 nwc::{
44 CreateNwcClientRequest, CreateNwcClientResponse,
45 ListNwcClientResponse, NostrPkStruct, UpdateNwcClientRequest,
46 UpdateNwcClientResponse,
47 },
48 },
49 rest::{POST, RequestBuilderExt, RestClient},
50 types::{
51 Empty,
52 payments::{MaybeBasicPaymentV2, VecBasicPaymentV1, VecBasicPaymentV2},
53 username::UsernameStruct,
54 },
55};
56use lexe_common::{
57 api::{
58 auth::{
59 BearerAuthRequestWire, BearerAuthResponse, BearerAuthToken,
60 LexeScope, TokenWithExpiration, UserSignupRequestWire,
61 UserSignupRequestWireV1,
62 },
63 fiat_rates::FiatRates,
64 models::{
65 SignMsgRequest, SignMsgResponse, VerifyMsgRequest,
66 VerifyMsgResponse,
67 },
68 provision::NodeProvisionRequest,
69 revocable_clients::{
70 CreateRevocableClientRequest, CreateRevocableClientResponse,
71 GetRevocableClients, RevocableClient, RevocableClients,
72 UpdateClientRequest, UpdateClientResponse,
73 },
74 user::UserPk,
75 version::{CurrentEnclaves, EnclavesToProvision, NodeEnclave},
76 },
77 byte_str::ByteStr,
78 constants::{self, node_provision_dns},
79 env::DeployEnv,
80};
81use lexe_crypto::{ed25519, rng::Crng};
82use lexe_enclave::enclave::Measurement;
83use lexe_tls::{attest_client, lexe_ca, rustls};
84use reqwest::Url;
85
86use crate::credentials::{ClientCredentials, CredentialsRef};
87
88#[derive(Clone)]
90pub struct GatewayClient {
91 rest: RestClient,
92 gateway_url: Cow<'static, str>,
93}
94
95#[derive(Clone)]
109pub struct NodeClient {
110 inner: Arc<NodeClientInner>,
111}
112
113struct NodeClientInner {
114 user_pk: Option<UserPk>,
116 gateway_client: GatewayClient,
117 run_rest: ArcSwapOption<RunRestClient>,
134 run_url: &'static str,
135 use_sgx: bool,
136 deploy_env: DeployEnv,
137 authenticator: Arc<BearerAuthenticator>,
138 tls_config: rustls::ClientConfig,
139}
140
141struct RunRestClient {
144 client: RestClient,
145 token_expiration: Option<SystemTime>,
148}
149
150impl GatewayClient {
153 pub fn new(
154 deploy_env: DeployEnv,
155 gateway_url: impl Into<Cow<'static, str>>,
156 user_agent: impl Into<Cow<'static, str>>,
157 ) -> anyhow::Result<Self> {
158 fn inner(
159 deploy_env: DeployEnv,
160 gateway_url: Cow<'static, str>,
161 user_agent: Cow<'static, str>,
162 ) -> anyhow::Result<GatewayClient> {
163 let tls_config = lexe_ca::app_gateway_client_config(deploy_env);
164 let rest = RestClient::new(user_agent, "gateway", tls_config);
165 Ok(GatewayClient { rest, gateway_url })
166 }
167 inner(deploy_env, gateway_url.into(), user_agent.into())
168 }
169}
170
171impl AppBackendApi for GatewayClient {
172 async fn signup_v2(
173 &self,
174 signed_req: &ed25519::Signed<&UserSignupRequestWire>,
175 ) -> Result<Empty, BackendApiError> {
176 let gateway_url = &self.gateway_url;
177 let req = self
178 .rest
179 .builder(POST, format!("{gateway_url}/app/v2/signup"))
180 .signed_bcs(signed_req)
181 .map_err(BackendApiError::bcs_serialize)?;
182 self.rest.send(req).await
183 }
184
185 async fn signup_v1(
186 &self,
187 _signed_req: &ed25519::Signed<&UserSignupRequestWireV1>,
188 ) -> Result<Empty, BackendApiError> {
189 debug_assert!(false, "Use `signup_v2`");
190 Err(BackendApiError::not_found("Use `/app/v2/signup`"))
191 }
192}
193
194#[async_trait]
195impl BearerAuthBackendApi for GatewayClient {
196 async fn bearer_auth(
197 &self,
198 signed_req: &ed25519::Signed<&BearerAuthRequestWire>,
199 ) -> Result<BearerAuthResponse, BackendApiError> {
200 let gateway_url = &self.gateway_url;
201 let req = self
202 .rest
203 .builder(POST, format!("{gateway_url}/app/bearer_auth"))
204 .signed_bcs(signed_req)
205 .map_err(BackendApiError::bcs_serialize)?;
206 self.rest.send(req).await
207 }
208}
209
210impl AppGatewayApi for GatewayClient {
211 async fn get_fiat_rates(&self) -> Result<FiatRates, GatewayApiError> {
212 let gateway_url = &self.gateway_url;
213 let req = self
214 .rest
215 .get(format!("{gateway_url}/app/v1/fiat_rates"), &Empty {});
216 self.rest.send(req).await
217 }
218
219 async fn enclaves_to_provision(
220 &self,
221 req: &EnclavesToProvisionRequest,
222 auth: BearerAuthToken,
223 ) -> Result<EnclavesToProvision, GatewayApiError> {
224 let gateway_url = &self.gateway_url;
225 let url = format!("{gateway_url}/app/v1/enclaves_to_provision");
226 let req = self.rest.post(url, req).bearer_auth(&auth);
227 self.rest.send(req).await
228 }
229
230 async fn latest_release(&self) -> Result<NodeEnclave, GatewayApiError> {
231 let gateway_url = &self.gateway_url;
232 let req = self
233 .rest
234 .get(format!("{gateway_url}/app/v1/latest_release"), &Empty {});
235 self.rest.send(req).await
236 }
237
238 async fn current_releases(
239 &self,
240 ) -> Result<CurrentEnclaves, GatewayApiError> {
241 let gateway_url = &self.gateway_url;
242 let req = self
243 .rest
244 .get(format!("{gateway_url}/app/v1/current_releases"), &Empty {});
245 self.rest.send(req).await
246 }
247
248 async fn current_enclaves(
249 &self,
250 ) -> Result<CurrentEnclaves, GatewayApiError> {
251 let gateway_url = &self.gateway_url;
252 let req = self
253 .rest
254 .get(format!("{gateway_url}/app/v1/current_enclaves"), &Empty {});
255 self.rest.send(req).await
256 }
257}
258
259impl NodeClient {
262 pub fn new(
263 rng: &mut impl Crng,
264 use_sgx: bool,
265 deploy_env: DeployEnv,
266 gateway_client: GatewayClient,
267 credentials: CredentialsRef<'_>,
268 ) -> anyhow::Result<Self> {
269 let run_url = constants::NODE_RUN_URL;
270
271 let gateway_url = &gateway_client.gateway_url;
272 ensure!(
273 gateway_url.starts_with("https://"),
274 "proxy connection must be https: gateway url: {gateway_url}",
275 );
276
277 let user_pk = credentials.user_pk();
278 let authenticator = credentials.bearer_authenticator()?;
279 let tls_config = credentials.tls_config(rng, deploy_env)?;
280 let run_rest = ArcSwapOption::from(None);
281
282 Ok(Self {
283 inner: Arc::new(NodeClientInner {
284 user_pk,
285 gateway_client,
286 run_rest,
287 run_url,
288 use_sgx,
289 deploy_env,
290 authenticator,
291 tls_config,
292 }),
293 })
294 }
295
296 pub fn user_pk(&self) -> Option<UserPk> {
301 self.inner.user_pk
302 }
303
304 async fn authed_run_rest(
313 &self,
314 ) -> Result<Arc<RunRestClient>, NodeApiError> {
315 let now = SystemTime::now();
316
317 if let Some(run_rest) = self.maybe_authed_run_rest(now) {
319 return Ok(run_rest);
320 }
321
322 let auth_token =
328 self.get_auth_token(now, LexeScope::GatewayProxy).await?;
329
330 if let Some(run_rest) = self.maybe_authed_run_rest(now) {
334 return Ok(run_rest);
336 }
337
338 let run_rest = RunRestClient::new(
340 &self.inner.gateway_client,
341 self.inner.run_url,
342 auth_token,
343 self.inner.tls_config.clone(),
344 )
345 .map_err(NodeApiError::bad_auth)?;
346 let run_rest = Arc::new(run_rest);
347
348 self.inner.run_rest.swap(Some(run_rest.clone()));
350
351 Ok(run_rest)
352 }
353
354 fn maybe_authed_run_rest(
357 &self,
358 now: SystemTime,
359 ) -> Option<Arc<RunRestClient>> {
360 let maybe_run_rest = self.inner.run_rest.load_full();
361 if let Some(run_rest) = maybe_run_rest
362 && !run_rest.token_needs_refresh(now)
363 {
364 Some(run_rest)
365 } else {
366 None
367 }
368 }
369
370 async fn get_auth_token(
373 &self,
374 now: SystemTime,
375 scope: LexeScope,
376 ) -> Result<TokenWithExpiration, NodeApiError> {
377 self.inner
378 .authenticator
379 .get_token_with_exp(&self.inner.gateway_client, now, scope)
380 .await
381 .map_err(|backend_error| {
384 let msg = format!("{backend_error:#}");
386
387 let BackendApiError {
388 data, sensitive, ..
389 } = backend_error;
390
391 NodeApiError {
392 kind: NodeErrorKind::BadAuth,
393 msg,
394 data,
395 sensitive,
396 }
397 })
398 }
399
400 fn provision_rest_client(
406 &self,
407 provision_url: &str,
408 auth_token: BearerAuthToken,
409 measurement: Measurement,
410 ) -> anyhow::Result<RestClient> {
411 let proxy = static_proxy_config(
412 &self.inner.gateway_client.gateway_url,
413 provision_url,
414 auth_token,
415 )
416 .context("Invalid proxy config")?;
417
418 let tls_config = attest_client::app_node_provision_client_config(
419 self.inner.use_sgx,
420 self.inner.deploy_env,
421 measurement,
422 );
423
424 let user_agent = self.inner.gateway_client.rest.user_agent().clone();
425 let (from, to) = (user_agent, "node-provision");
426 let reqwest_client = RestClient::client_builder(&from)
427 .proxy(proxy)
428 .use_preconfigured_tls(tls_config)
429 .timeout(Duration::from_secs(30))
431 .build()
432 .context("Failed to build client")?;
433
434 let provision_rest = RestClient::from_inner(reqwest_client, from, to);
435
436 Ok(provision_rest)
437 }
438
439 pub async fn create_client_credentials(
442 &self,
443 req: CreateRevocableClientRequest,
444 ) -> anyhow::Result<(RevocableClient, ClientCredentials)> {
445 let resp = self.create_revocable_client(req.clone()).await?;
447
448 let client = RevocableClient {
449 pubkey: resp.pubkey,
450 created_at: resp.created_at,
451 label: req.label,
452 scope: req.scope,
453 expires_at: req.expires_at,
454 is_revoked: false,
455 };
456
457 let client_credentials = ClientCredentials::from(resp);
458
459 Ok((client, client_credentials))
460 }
461
462 pub async fn get_gateway_token(&self) -> anyhow::Result<BearerAuthToken> {
467 let now = SystemTime::now();
468 self.inner
469 .authenticator
470 .get_token(&self.inner.gateway_client, now, LexeScope::GatewayProxy)
471 .await
472 .context("Failed to get gateway token")
473 }
474}
475
476impl AppNodeProvisionApi for NodeClient {
477 async fn provision(
478 &self,
479 measurement: Measurement,
480 data: NodeProvisionRequest,
481 ) -> Result<Empty, NodeApiError> {
482 let now = SystemTime::now();
483 let mr_short = measurement.short();
484 let provision_dns = node_provision_dns(&mr_short);
485 let provision_url = format!("https://{provision_dns}");
486
487 let auth_token = self
489 .get_auth_token(now, LexeScope::GatewayProxy)
490 .await?
491 .token;
492 let provision_rest = self
493 .provision_rest_client(&provision_url, auth_token, measurement)
494 .context("Failed to build provision rest client")
495 .map_err(NodeApiError::provision)?;
496
497 let req = provision_rest
498 .post(format!("{provision_url}/app/provision"), &data);
499 provision_rest.send(req).await
500 }
501}
502
503impl AppNodeRunApi for NodeClient {
504 async fn node_info(&self) -> Result<NodeInfo, NodeApiError> {
505 let run_rest = &self.authed_run_rest().await?.client;
506 let run_url = &self.inner.run_url;
507 let url = format!("{run_url}/app/v2/node_info");
508 let req = run_rest.get(url, &Empty {});
509 run_rest.send(req).await
510 }
511
512 async fn node_info_v1(&self) -> Result<NodeInfoV1, NodeApiError> {
513 let run_rest = &self.authed_run_rest().await?.client;
514 let run_url = &self.inner.run_url;
515 let url = format!("{run_url}/app/node_info");
516 let req = run_rest.get(url, &Empty {});
517 run_rest.send(req).await
518 }
519
520 async fn debug_info(&self) -> Result<DebugInfo, NodeApiError> {
521 let run_rest = &self.authed_run_rest().await?.client;
522 let run_url = &self.inner.run_url;
523 let url = format!("{run_url}/app/debug_info");
524 let req = run_rest.get(url, &Empty {});
525 run_rest.send(req).await
526 }
527
528 async fn list_channels(
529 &self,
530 ) -> Result<ListChannelsResponse, NodeApiError> {
531 let run_rest = &self.authed_run_rest().await?.client;
532 let run_url = &self.inner.run_url;
533 let url = format!("{run_url}/app/list_channels");
534 let req = run_rest.get(url, &Empty {});
535 run_rest.send(req).await
536 }
537
538 async fn sign_message(
539 &self,
540 data: SignMsgRequest,
541 ) -> Result<SignMsgResponse, NodeApiError> {
542 let run_rest = &self.authed_run_rest().await?.client;
543 let run_url = &self.inner.run_url;
544 let url = format!("{run_url}/app/sign_message");
545 let req = run_rest.post(url, &data);
546 run_rest.send(req).await
547 }
548
549 async fn verify_message(
550 &self,
551 data: VerifyMsgRequest,
552 ) -> Result<VerifyMsgResponse, NodeApiError> {
553 let run_rest = &self.authed_run_rest().await?.client;
554 let run_url = &self.inner.run_url;
555 let url = format!("{run_url}/app/verify_message");
556 let req = run_rest.post(url, &data);
557 run_rest.send(req).await
558 }
559
560 async fn open_channel(
561 &self,
562 data: OpenChannelRequest,
563 ) -> Result<OpenChannelResponse, NodeApiError> {
564 let run_rest = &self.authed_run_rest().await?.client;
565 let run_url = &self.inner.run_url;
566 let url = format!("{run_url}/app/open_channel");
567 let req = run_rest.post(url, &data);
568 run_rest.send(req).await
569 }
570
571 async fn preflight_open_channel(
572 &self,
573 data: PreflightOpenChannelRequest,
574 ) -> Result<PreflightOpenChannelResponse, NodeApiError> {
575 let run_rest = &self.authed_run_rest().await?.client;
576 let run_url = &self.inner.run_url;
577 let url = format!("{run_url}/app/preflight_open_channel");
578 let req = run_rest.post(url, &data);
579 run_rest.send(req).await
580 }
581
582 async fn close_channel(
583 &self,
584 data: CloseChannelRequest,
585 ) -> Result<Empty, NodeApiError> {
586 let run_rest = &self.authed_run_rest().await?.client;
587 let run_url = &self.inner.run_url;
588 let url = format!("{run_url}/app/close_channel");
589 let req = run_rest.post(url, &data);
590 run_rest.send(req).await
591 }
592
593 async fn preflight_close_channel(
594 &self,
595 data: PreflightCloseChannelRequest,
596 ) -> Result<PreflightCloseChannelResponse, NodeApiError> {
597 let run_rest = &self.authed_run_rest().await?.client;
598 let run_url = &self.inner.run_url;
599 let url = format!("{run_url}/app/preflight_close_channel");
600 let req = run_rest.post(url, &data);
601 run_rest.send(req).await
602 }
603
604 async fn create_invoice(
605 &self,
606 data: CreateInvoiceRequest,
607 ) -> Result<CreateInvoiceResponse, NodeApiError> {
608 let run_rest = &self.authed_run_rest().await?.client;
609 let run_url = &self.inner.run_url;
610 let url = format!("{run_url}/app/create_invoice");
611 let req = run_rest.post(url, &data);
612 run_rest.send(req).await
613 }
614
615 async fn pay_invoice(
616 &self,
617 req: PayInvoiceRequest,
618 ) -> Result<PayInvoiceResponse, NodeApiError> {
619 let run_rest = &self.authed_run_rest().await?.client;
620 let run_url = &self.inner.run_url;
621 let url = format!("{run_url}/app/pay_invoice");
622 let req = run_rest
624 .post(url, &req)
625 .timeout(constants::MAX_FLOW_TIMEOUT + Duration::from_secs(2));
626 run_rest.send(req).await
627 }
628
629 async fn preflight_pay_invoice(
630 &self,
631 req: PreflightPayInvoiceRequest,
632 ) -> Result<PreflightPayInvoiceResponse, NodeApiError> {
633 let run_rest = &self.authed_run_rest().await?.client;
634 let run_url = &self.inner.run_url;
635 let url = format!("{run_url}/app/preflight_pay_invoice");
636 let req = run_rest
638 .post(url, &req)
639 .timeout(constants::MAX_FLOW_TIMEOUT + Duration::from_secs(2));
640 run_rest.send(req).await
641 }
642
643 async fn pay_onchain(
644 &self,
645 req: PayOnchainRequest,
646 ) -> Result<PayOnchainResponse, NodeApiError> {
647 let run_rest = &self.authed_run_rest().await?.client;
648 let run_url = &self.inner.run_url;
649 let url = format!("{run_url}/app/pay_onchain");
650 let req = run_rest.post(url, &req);
651 run_rest.send(req).await
652 }
653
654 async fn preflight_pay_onchain(
655 &self,
656 req: PreflightPayOnchainRequest,
657 ) -> Result<PreflightPayOnchainResponse, NodeApiError> {
658 let run_rest = &self.authed_run_rest().await?.client;
659 let run_url = &self.inner.run_url;
660 let url = format!("{run_url}/app/preflight_pay_onchain");
661 let req = run_rest.post(url, &req);
662 run_rest.send(req).await
663 }
664
665 async fn create_offer(
666 &self,
667 req: CreateOfferRequest,
668 ) -> Result<CreateOfferResponse, NodeApiError> {
669 let run_rest = &self.authed_run_rest().await?.client;
670 let run_url = &self.inner.run_url;
671 let url = format!("{run_url}/app/create_offer");
672 let req = run_rest.post(url, &req);
673 run_rest.send(req).await
674 }
675
676 async fn pay_offer(
677 &self,
678 req: PayOfferRequest,
679 ) -> Result<PayOfferResponse, NodeApiError> {
680 let run_rest = &self.authed_run_rest().await?.client;
681 let run_url = &self.inner.run_url;
682 let url = format!("{run_url}/app/pay_offer");
683 let req = run_rest.post(url, &req);
684 run_rest.send(req).await
685 }
686
687 async fn preflight_pay_offer(
688 &self,
689 req: PreflightPayOfferRequest,
690 ) -> Result<PreflightPayOfferResponse, NodeApiError> {
691 let run_rest = &self.authed_run_rest().await?.client;
692 let run_url = &self.inner.run_url;
693 let url = format!("{run_url}/app/preflight_pay_offer");
694 let req = run_rest.post(url, &req);
695 run_rest.send(req).await
696 }
697
698 async fn get_address(&self) -> Result<GetAddressResponse, NodeApiError> {
699 let run_rest = &self.authed_run_rest().await?.client;
700 let run_url = &self.inner.run_url;
701 let url = format!("{run_url}/app/get_address");
702 let req = run_rest.post(url, &Empty {});
703 run_rest.send(req).await
704 }
705
706 async fn get_payments_by_indexes(
707 &self,
708 _: PaymentCreatedIndexes,
709 ) -> Result<VecBasicPaymentV1, NodeApiError> {
710 unimplemented!("Deprecated")
711 }
712
713 async fn get_new_payments(
714 &self,
715 _: GetNewPayments,
716 ) -> Result<VecBasicPaymentV1, NodeApiError> {
717 unimplemented!("Deprecated")
718 }
719
720 async fn get_updated_payments(
721 &self,
722 req: GetUpdatedPayments,
723 ) -> Result<VecBasicPaymentV2, NodeApiError> {
724 let run_rest = &self.authed_run_rest().await?.client;
725 let run_url = &self.inner.run_url;
726 let url = format!("{run_url}/app/payments/updated");
727 let req = run_rest.get(url, &req);
728 run_rest.send(req).await
729 }
730
731 async fn get_payment_by_id(
732 &self,
733 req: PaymentIdStruct,
734 ) -> Result<MaybeBasicPaymentV2, NodeApiError> {
735 let run_rest = &self.authed_run_rest().await?.client;
736 let run_url = &self.inner.run_url;
737 let url = format!("{run_url}/app/v1/payments/id");
738 let req = run_rest.get(url, &req);
739 run_rest.send(req).await
740 }
741
742 async fn update_personal_note(
743 &self,
744 req: UpdatePersonalNote,
745 ) -> Result<Empty, NodeApiError> {
746 let run_rest = &self.authed_run_rest().await?.client;
747 let run_url = &self.inner.run_url;
748 let url = format!("{run_url}/app/payments/note");
749 let req = run_rest.put(url, &req);
750 run_rest.send(req).await
751 }
752
753 async fn get_revocable_clients(
754 &self,
755 req: GetRevocableClients,
756 ) -> Result<RevocableClients, NodeApiError> {
757 let run_rest = &self.authed_run_rest().await?.client;
758 let run_url = &self.inner.run_url;
759 let url = format!("{run_url}/app/clients");
760 let req = run_rest.get(url, &req);
761 run_rest.send(req).await
762 }
763
764 async fn create_revocable_client(
765 &self,
766 req: CreateRevocableClientRequest,
767 ) -> Result<CreateRevocableClientResponse, NodeApiError> {
768 let run_rest = &self.authed_run_rest().await?.client;
769 let run_url = &self.inner.run_url;
770 let url = format!("{run_url}/app/clients");
771 let req = run_rest.post(url, &req);
772 run_rest.send(req).await
773 }
774
775 async fn update_revocable_client(
776 &self,
777 req: UpdateClientRequest,
778 ) -> Result<UpdateClientResponse, NodeApiError> {
779 let run_rest = &self.authed_run_rest().await?.client;
780 let run_url = &self.inner.run_url;
781 let url = format!("{run_url}/app/clients");
782 let req = run_rest.put(url, &req);
783 run_rest.send(req).await
784 }
785
786 async fn list_broadcasted_txs(
787 &self,
788 ) -> Result<serde_json::Value, NodeApiError> {
789 let run_rest = &self.authed_run_rest().await?.client;
790 let run_url = &self.inner.run_url;
791 let url = format!("{run_url}/app/list_broadcasted_txs");
792 let req = run_rest.get(url, &Empty {});
793 run_rest.send(req).await
794 }
795
796 async fn backup_info(&self) -> Result<BackupInfo, NodeApiError> {
797 let run_rest = &self.authed_run_rest().await?.client;
798 let run_url = &self.inner.run_url;
799 let url = format!("{run_url}/app/backup");
800 let req = run_rest.get(url, &Empty {});
801 run_rest.send(req).await
802 }
803
804 async fn setup_gdrive(
805 &self,
806 req: SetupGDrive,
807 ) -> Result<Empty, NodeApiError> {
808 let run_rest = &self.authed_run_rest().await?.client;
809 let run_url = &self.inner.run_url;
810 let url = format!("{run_url}/app/backup/gdrive");
811 let req = run_rest.post(url, &req);
812 run_rest.send(req).await
813 }
814
815 async fn get_human_bitcoin_address(
816 &self,
817 ) -> Result<GetHumanBitcoinAddressResponse, NodeApiError> {
818 let run_rest = &self.authed_run_rest().await?.client;
819 let run_url = &self.inner.run_url;
820 let url = format!("{run_url}/app/v2/human_bitcoin_address");
821 let req = run_rest.get(url, &Empty {});
822 run_rest.send(req).await
823 }
824
825 async fn get_human_bitcoin_address_v1(
826 &self,
827 ) -> Result<HumanBitcoinAddressV1, NodeApiError> {
828 let run_rest = &self.authed_run_rest().await?.client;
829 let run_url = &self.inner.run_url;
830 let url = format!("{run_url}/app/human_bitcoin_address");
831 let req = run_rest.get(url, &Empty {});
832 run_rest.send(req).await
833 }
834
835 async fn upsert_custom_human_bitcoin_address(
836 &self,
837 req: UsernameStruct,
838 ) -> Result<UpsertHumanBitcoinAddressResponse, NodeApiError> {
839 let run_rest = &self.authed_run_rest().await?.client;
840 let run_url = &self.inner.run_url;
841 let url = format!("{run_url}/app/v2/human_bitcoin_address");
842 let req = run_rest.put(url, &req);
843 run_rest.send(req).await
844 }
845
846 async fn update_human_bitcoin_address_v1(
847 &self,
848 req: UsernameStruct,
849 ) -> Result<HumanBitcoinAddressV1, NodeApiError> {
850 let run_rest = &self.authed_run_rest().await?.client;
851 let run_url = &self.inner.run_url;
852 let url = format!("{run_url}/app/human_bitcoin_address");
853 let req = run_rest.put(url, &req);
854 run_rest.send(req).await
855 }
856
857 #[allow(deprecated)]
858 async fn get_payment_address_v1(
859 &self,
860 ) -> Result<HumanBitcoinAddressV1, NodeApiError> {
861 self.get_human_bitcoin_address_v1().await
862 }
863
864 #[allow(deprecated)]
865 async fn update_payment_address_v1(
866 &self,
867 req: UsernameStruct,
868 ) -> Result<HumanBitcoinAddressV1, NodeApiError> {
869 self.update_human_bitcoin_address_v1(req).await
870 }
871
872 async fn list_nwc_clients(
873 &self,
874 ) -> Result<ListNwcClientResponse, NodeApiError> {
875 let run_rest = &self.authed_run_rest().await?.client;
876 let run_url = &self.inner.run_url;
877 let url = format!("{run_url}/app/nwc_clients");
878 let req = run_rest.get(url, &Empty {});
879 run_rest.send(req).await
880 }
881
882 async fn create_nwc_client(
883 &self,
884 req: CreateNwcClientRequest,
885 ) -> Result<CreateNwcClientResponse, NodeApiError> {
886 let run_rest = &self.authed_run_rest().await?.client;
887 let run_url = &self.inner.run_url;
888 let url = format!("{run_url}/app/nwc_clients");
889 let req = run_rest.post(url, &req);
890 run_rest.send(req).await
891 }
892
893 async fn update_nwc_client(
894 &self,
895 req: UpdateNwcClientRequest,
896 ) -> Result<UpdateNwcClientResponse, NodeApiError> {
897 let run_rest = &self.authed_run_rest().await?.client;
898 let run_url = &self.inner.run_url;
899 let url = format!("{run_url}/app/nwc_clients");
900 let req = run_rest.put(url, &req);
901 run_rest.send(req).await
902 }
903
904 async fn delete_nwc_client(
905 &self,
906 req: NostrPkStruct,
907 ) -> Result<Empty, NodeApiError> {
908 let run_rest = &self.authed_run_rest().await?.client;
909 let run_url = &self.inner.run_url;
910 let url = format!("{run_url}/app/nwc_clients");
911 let req = run_rest.delete(url, &req);
912 run_rest.send(req).await
913 }
914}
915
916impl RunRestClient {
919 fn new(
920 gateway_client: &GatewayClient,
921 run_url: &str,
922 auth_token: TokenWithExpiration,
923 tls_config: rustls::ClientConfig,
924 ) -> anyhow::Result<Self> {
925 let TokenWithExpiration { expiration, token } = auth_token;
926 let (from, to) = (gateway_client.rest.user_agent().clone(), "node-run");
927 let proxy =
928 static_proxy_config(&gateway_client.gateway_url, run_url, token)?;
929 let client = RestClient::client_builder(&from)
930 .proxy(proxy)
931 .use_preconfigured_tls(tls_config.clone())
932 .build()
933 .context("Failed to build client")?;
934 let client = RestClient::from_inner(client, from, to);
935
936 Ok(Self {
937 client,
938 token_expiration: expiration,
939 })
940 }
941
942 fn token_needs_refresh(&self, now: SystemTime) -> bool {
945 auth::helpers::token_needs_refresh(now, self.token_expiration)
946 }
947}
948
949fn static_proxy_config(
971 gateway_url: &str,
972 node_url: &str,
973 auth_token: BearerAuthToken,
974) -> anyhow::Result<reqwest::Proxy> {
975 let node_url = Url::parse(node_url).context("Invalid node url")?;
976 let gateway_url = gateway_url.to_owned();
977
978 let auth_header = http::HeaderValue::from_maybe_shared(ByteStr::from(
980 format!("Bearer {auth_token}"),
981 ))?;
982
983 let proxy = reqwest::Proxy::custom(move |url| {
984 if url_base_eq(url, &node_url) {
986 Some(gateway_url.clone())
987 } else {
988 None
989 }
990 })
991 .custom_http_auth(auth_header);
994
995 Ok(proxy)
996}
997
998fn url_base_eq(u1: &Url, u2: &Url) -> bool {
999 u1.scheme() == u2.scheme()
1000 && u1.host() == u2.host()
1001 && u1.port_or_known_default() == u2.port_or_known_default()
1002}
1003
1004#[cfg(test)]
1005mod test {
1006 use super::*;
1007
1008 #[test]
1009 fn test_url_base_eq() {
1010 let eq_classes = vec![
1013 vec![
1014 "https://hello.world",
1015 "https://hello.world/",
1016 "https://hello.world/my_cool_method",
1017 "https://hello.world/my_cool_method&query=params",
1018 "https://hello.world/&query=params",
1019 ],
1020 vec![
1021 "http://hello.world",
1022 "http://hello.world/",
1023 "http://hello.world/my_cool_method",
1024 "http://hello.world/my_cool_method&query=params",
1025 "http://hello.world/&query=params",
1026 ],
1027 vec![
1028 "https://hello.world:8080",
1029 "https://hello.world:8080/",
1030 "https://hello.world:8080/my_cool_method",
1031 "https://hello.world:8080/my_cool_method&query=params",
1032 "https://hello.world:8080/&query=params",
1033 ],
1034 vec![
1035 "https://127.0.0.1:8080",
1036 "https://127.0.0.1:8080/",
1037 "https://127.0.0.1:8080/my_cool_method",
1038 "https://127.0.0.1:8080/my_cool_method&query=params",
1039 "https://127.0.0.1:8080/&query=params",
1040 ],
1041 vec![
1042 "https://[::1]:8080",
1043 "https://[::1]:8080/",
1044 "https://[::1]:8080/my_cool_method",
1045 "https://[::1]:8080/my_cool_method&query=params",
1046 "https://[::1]:8080/&query=params",
1047 ],
1048 ];
1049
1050 let eq_classes = eq_classes
1051 .into_iter()
1052 .map(|eq_class| {
1053 eq_class
1054 .into_iter()
1055 .map(|url| Url::parse(url).unwrap())
1056 .collect::<Vec<_>>()
1057 })
1058 .collect::<Vec<_>>();
1059
1060 let n_classes = eq_classes.len();
1061 let n_urls = eq_classes[0].len();
1062
1063 for eq_class in &eq_classes {
1065 for idx_u1 in 0..n_urls {
1066 for idx_u2 in idx_u1..n_urls {
1068 let u1 = &eq_class[idx_u1];
1069 let u2 = &eq_class[idx_u2];
1070 assert!(url_base_eq(u1, u2));
1071 assert!(url_base_eq(u2, u1));
1073 }
1074 }
1075 }
1076
1077 for idx_class1 in 0..(n_classes - 1) {
1079 let eq_class1 = &eq_classes[idx_class1];
1080 for eq_class2 in eq_classes.iter().skip(idx_class1 + 1) {
1081 for u1 in eq_class1 {
1082 for u2 in eq_class2 {
1083 assert!(!url_base_eq(u1, u2));
1085 assert!(!url_base_eq(u2, u1));
1086 }
1087 }
1088 }
1089 }
1090 }
1091}