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