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