1use crate::{EncryptedPayload, SdkError, SdkResult};
2use futures::{AsyncWrite, AsyncWriteExt, StreamExt};
3use lit_node_core::NodeSet;
4use serde::Serialize;
5use serde::de::DeserializeOwned;
6use std::collections::HashMap;
7use std::fmt;
8use std::fmt::{Display, Formatter};
9use std::marker::PhantomData;
10use std::str::FromStr;
11use uuid::Uuid;
12
13#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
15pub enum UrlPrefix {
16 Http,
18 #[default]
19 Https,
21}
22
23impl Display for UrlPrefix {
24 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
25 match self {
26 Self::Http => write!(f, "http"),
27 Self::Https => write!(f, "https"),
28 }
29 }
30}
31
32impl FromStr for UrlPrefix {
33 type Err = SdkError;
34
35 fn from_str(s: &str) -> Result<Self, Self::Err> {
36 match s.to_lowercase().as_str() {
37 "http" => Ok(Self::Http),
38 "https" => Ok(Self::Https),
39 _ => Err(SdkError::Parse(format!(
40 "invalid url prefix '{}'. Expected 'http' or 'https'",
41 s
42 ))),
43 }
44 }
45}
46
47#[derive(Clone, Debug)]
49pub struct EndpointRequest<T>
50where
51 T: Serialize + DeserializeOwned + Sync,
52{
53 pub node_set: NodeSet,
55 pub identity_key: NodeIdentityKey,
57 pub body: T,
59}
60
61pub type NodeIdentityKey = [u8; 32];
63
64#[derive(Clone, Debug)]
66pub struct AdminRequest<B, T, R>
67where
68 B: Sized + Default,
69 T: Serialize + DeserializeOwned,
70 R: Serialize + DeserializeOwned,
71{
72 pub(crate) url_prefix: UrlPrefix,
73 pub(crate) api_path: &'static str,
74 pub(crate) custom_headers: HashMap<String, String>,
75 pub(crate) public_address: String,
76 pub(crate) inner: T,
77 pub(crate) _builder: PhantomData<B>,
78 pub(crate) _response: PhantomData<R>,
79}
80
81impl<B, T, R> AdminRequest<B, T, R>
82where
83 B: Sized + Default,
84 T: Serialize + DeserializeOwned,
85 R: Serialize + DeserializeOwned,
86{
87 #[allow(clippy::new_ret_no_self)]
89 pub fn new() -> B {
90 B::default()
91 }
92
93 pub fn url_prefix(&self) -> &UrlPrefix {
95 &self.url_prefix
96 }
97
98 pub fn url_suffix(&self) -> &'static str {
100 self.api_path
101 }
102
103 pub fn public_address(&self) -> &str {
105 &self.public_address
106 }
107
108 pub fn custom_headers(&self) -> &HashMap<String, String> {
110 &self.custom_headers
111 }
112
113 pub fn inner(&self) -> &T {
115 &self.inner
116 }
117
118 pub async fn send(&self) -> SdkResult<AdminResponse<R>> {
120 let response_output = crate::request(
121 self.url_prefix,
122 &self.public_address,
123 self.api_path,
124 "",
125 &self.custom_headers,
126 &self.inner,
127 )
128 .await?;
129 let out_headers = crate::extract_headers(&response_output)?;
130 let output = response_output.text().await?;
131 let response = serde_json::from_str(&output)?;
132 Ok(AdminResponse {
133 headers: out_headers,
134 results: response,
135 })
136 }
137
138 pub async fn download<W>(&self, mut output: W) -> SdkResult<AdminResponse<()>>
140 where
141 W: AsyncWrite + Unpin,
142 {
143 let response = crate::request(
144 self.url_prefix,
145 &self.public_address,
146 self.api_path,
147 "",
148 &self.custom_headers,
149 &self.inner,
150 )
151 .await?;
152 let out_headers = crate::extract_headers(&response)?;
153 let mut stream = response.bytes_stream();
154 while let Some(chunk) = stream.next().await {
155 let bytes = chunk?;
156 output.write_all(&bytes).await?;
157 output.flush().await?;
158 }
159 Ok(AdminResponse {
160 headers: out_headers,
161 results: (),
162 })
163 }
164}
165
166#[derive(Clone, Debug)]
168pub struct AdminResponse<R>
169where
170 R: Serialize + DeserializeOwned,
171{
172 pub(crate) headers: HashMap<String, String>,
173 pub(crate) results: R,
174}
175
176impl<R> AdminResponse<R>
177where
178 R: Serialize + DeserializeOwned,
179{
180 pub fn results(&self) -> &R {
182 &self.results
183 }
184
185 pub fn headers(&self) -> &HashMap<String, String> {
187 &self.headers
188 }
189}
190
191#[derive(Clone, Debug)]
193pub struct Request<B, T, R>
194where
195 B: Sized + Default,
196 T: Serialize + DeserializeOwned,
197 R: Serialize + DeserializeOwned,
198{
199 pub(crate) url_prefix: UrlPrefix,
200 pub(crate) api_path: &'static str,
201 pub(crate) node_set: Vec<NodeSet>,
202 pub(crate) custom_headers: HashMap<String, String>,
203 pub(crate) inner: T,
204 pub(crate) request_id: Uuid,
205 pub(crate) _builder: PhantomData<B>,
206 pub(crate) _response: PhantomData<R>,
207}
208
209impl<B, T, R> Request<B, T, R>
210where
211 B: Sized + Default,
212 T: Serialize + DeserializeOwned,
213 R: Serialize + DeserializeOwned,
214{
215 #[allow(clippy::new_ret_no_self)]
217 pub fn new() -> B {
218 B::default()
219 }
220
221 pub fn url_prefix(&self) -> &UrlPrefix {
223 &self.url_prefix
224 }
225
226 pub fn url_suffix(&self) -> &'static str {
228 self.api_path
229 }
230
231 pub fn node_set(&self) -> &[NodeSet] {
233 &self.node_set
234 }
235
236 pub fn custom_headers(&self) -> &HashMap<String, String> {
238 &self.custom_headers
239 }
240
241 pub fn inner(&self) -> &T {
243 &self.inner
244 }
245
246 pub fn request_id(&self) -> &Uuid {
248 &self.request_id
249 }
250
251 pub async fn send(&self) -> SdkResult<Response<R>> {
253 let mut headers = Vec::with_capacity(self.node_set.len());
254 let mut responses = Vec::with_capacity(self.node_set.len());
255 let request_id = self.request_id.to_string();
256 let mut requests = Vec::with_capacity(self.node_set.len());
257
258 for node in &self.node_set {
259 requests.push(crate::request(
260 self.url_prefix,
261 &node.socket_address,
262 self.api_path,
263 &request_id,
264 &self.custom_headers,
265 &self.inner,
266 ));
267 }
268 let results = futures::future::join_all(requests).await;
269 for result in results {
270 let response_output = result?;
271 let out_headers = crate::extract_headers(&response_output)?;
272 headers.push(out_headers);
273 let output = response_output.text().await?;
274 responses.push(serde_json::from_str(&output)?);
275 }
276 Ok(Response {
277 headers,
278 results: responses,
279 })
280 }
281}
282
283#[derive(Clone, Debug)]
286pub struct EncryptedBroadcastRequest<B, T, R>
287where
288 B: Sized + Default,
289 T: Serialize + DeserializeOwned + Sync,
290 R: Serialize + DeserializeOwned + Sync,
291{
292 pub(crate) url_prefix: UrlPrefix,
293 pub(crate) api_path: &'static str,
294 pub(crate) node_set: HashMap<NodeSet, NodeIdentityKey>,
295 pub(crate) custom_headers: HashMap<String, String>,
296 pub(crate) inner: T,
297 pub(crate) request_id: Uuid,
298 pub(crate) _builder: PhantomData<B>,
299 pub(crate) _response: PhantomData<R>,
300}
301
302impl<B, T, R> EncryptedBroadcastRequest<B, T, R>
303where
304 B: Sized + Default,
305 T: Serialize + DeserializeOwned + Sync,
306 R: Serialize + DeserializeOwned + Sync,
307{
308 #[allow(clippy::new_ret_no_self)]
310 pub fn new() -> B {
311 B::default()
312 }
313
314 pub fn url_prefix(&self) -> &UrlPrefix {
316 &self.url_prefix
317 }
318
319 pub fn url_suffix(&self) -> &'static str {
321 self.api_path
322 }
323
324 pub fn node_set(&self) -> &HashMap<NodeSet, NodeIdentityKey> {
326 &self.node_set
327 }
328
329 pub fn custom_headers(&self) -> &HashMap<String, String> {
331 &self.custom_headers
332 }
333
334 pub fn inner(&self) -> &T {
336 &self.inner
337 }
338
339 pub fn request_id(&self) -> &Uuid {
341 &self.request_id
342 }
343
344 pub async fn send(&self, my_secret_key: &[u8; 32]) -> SdkResult<Response<R>> {
346 let mut headers = Vec::with_capacity(self.node_set.len());
347 let mut responses = Vec::with_capacity(self.node_set.len());
348 let body = serde_json::to_vec(&self.inner)?;
349 let request_id = self.request_id.to_string();
350 let mut requests = Vec::with_capacity(self.node_set.len());
351
352 for (node, key) in &self.node_set {
353 let payload = EncryptedPayload::<T>::encrypt(my_secret_key, key, &body);
354 requests.push(crate::request(
355 self.url_prefix,
356 &node.socket_address,
357 self.api_path,
358 &request_id,
359 &self.custom_headers,
360 payload,
361 ));
362 }
363 let results = futures::future::join_all(requests).await;
364 for result in results {
365 let response_output = result?;
366 let out_headers = crate::extract_headers(&response_output)?;
367 headers.push(out_headers);
368 let output = response_output.text().await?;
369 let des = serde_json::from_str::<EncryptedPayload<R>>(&output)?;
370 let (r, _) = des.json_decrypt(my_secret_key)?;
371 responses.push(r);
372 }
373
374 Ok(Response {
375 headers,
376 results: responses,
377 })
378 }
379}
380
381#[derive(Clone, Debug)]
384pub struct EncryptedMulticastRequest<B, T, R>
385where
386 B: Sized + Default,
387 T: Serialize + DeserializeOwned + Sync,
388 R: Serialize + DeserializeOwned + Sync,
389{
390 pub(crate) url_prefix: UrlPrefix,
391 pub(crate) api_path: &'static str,
392 pub(crate) node_set: Vec<EndpointRequest<T>>,
393 pub(crate) custom_headers: HashMap<String, String>,
394 pub(crate) request_id: Uuid,
395 pub(crate) _builder: PhantomData<B>,
396 pub(crate) _response: PhantomData<R>,
397}
398
399impl<B, T, R> EncryptedMulticastRequest<B, T, R>
400where
401 B: Sized + Default,
402 T: Serialize + DeserializeOwned + Sync,
403 R: Serialize + DeserializeOwned + Sync,
404{
405 #[allow(clippy::new_ret_no_self)]
407 pub fn new() -> B {
408 B::default()
409 }
410
411 pub fn url_prefix(&self) -> &UrlPrefix {
413 &self.url_prefix
414 }
415
416 pub fn url_suffix(&self) -> &'static str {
418 self.api_path
419 }
420
421 pub fn node_set(&self) -> &[EndpointRequest<T>] {
423 &self.node_set
424 }
425
426 pub fn custom_headers(&self) -> &HashMap<String, String> {
428 &self.custom_headers
429 }
430
431 pub fn request_id(&self) -> &Uuid {
433 &self.request_id
434 }
435
436 pub async fn send(&self, my_secret_key: &[u8; 32]) -> SdkResult<Response<R>> {
438 let mut headers = Vec::with_capacity(self.node_set.len());
439 let mut responses = Vec::with_capacity(self.node_set.len());
440 let request_id = self.request_id.to_string();
441 let mut requests = Vec::with_capacity(self.node_set.len());
442
443 for endpoint in &self.node_set {
444 let body = serde_json::to_vec(&endpoint.body)?;
445 let payload =
446 EncryptedPayload::<T>::encrypt(my_secret_key, &endpoint.identity_key, &body);
447 requests.push(crate::request(
448 self.url_prefix,
449 &endpoint.node_set.socket_address,
450 self.api_path,
451 &request_id,
452 &self.custom_headers,
453 payload,
454 ));
455 }
456 let results = futures::future::join_all(requests).await;
457 for result in results {
458 let response_output = result?;
459 let out_headers = crate::extract_headers(&response_output)?;
460 headers.push(out_headers);
461 let output = response_output.text().await?;
462 let des = serde_json::from_str::<EncryptedPayload<R>>(&output)?;
463 let (r, _) = des.json_decrypt(my_secret_key)?;
464 responses.push(r);
465 }
466
467 Ok(Response {
468 headers,
469 results: responses,
470 })
471 }
472}
473
474#[derive(Clone, Debug)]
476pub struct Response<T>
477where
478 T: Serialize + DeserializeOwned,
479{
480 pub(crate) results: Vec<T>,
481 pub(crate) headers: Vec<HashMap<String, String>>,
482}
483
484impl<T> Response<T>
485where
486 T: Serialize + DeserializeOwned,
487{
488 pub fn results(&self) -> &Vec<T> {
492 &self.results
493 }
494
495 pub fn headers(&self) -> &Vec<HashMap<String, String>> {
499 &self.headers
500 }
501}