1use redgold_schema::message::{Request, Response};
2use redgold_schema::{error_info, structs, ErrorInfoContext, RgResult, SafeOption};
3use redgold_schema::structs::{AboutNodeRequest, AboutNodeResponse, Address, AddressInfo, CurrencyAmount, ErrorInfo, GetActivePartyKeyRequest, GetPeersInfoRequest, HashSearchRequest, HashSearchResponse, NetworkEnvironment, NodeMetadata, PublicKey, Seed, SubmitTransactionRequest, SubmitTransactionResponse, Transaction};
4use std::time::Duration;
5use redgold_schema::explorer::DetailedAddress;
6use std::collections::HashMap;
7use serde::Serialize;
8use serde::de::DeserializeOwned;
9use tracing::debug;
10use uuid::Uuid;
11use redgold_schema::helpers::easy_json::{EasyJson, EasyJsonDeser};
12use redgold_schema::helpers::with_metadata_hashable::WithMetadataHashable;
13use redgold_schema::observability::errors::{EnhanceErrorInfo, Loggable};
14use redgold_schema::party::party_internal_data::PartyInternalData;
15use redgold_schema::proto_serde::ProtoSerde;
16use redgold_schema::util::lang_util::WithMaxLengthString;
17use redgold_schema::errors::into_error::ToErrorInfo;
18
19
20#[cfg(not(target_arch = "wasm32"))]
21use reqwest::ClientBuilder;
22
23
24pub trait RequestResponseAuth: Send + Sync {
25 fn sign_request(&self, r: &Request) -> RgResult<Request>;
26 fn verify(&self, response: Response, intended_pk: Option<&PublicKey>) -> RgResult<Response>;
27
28 fn clone_box(&self) -> Box<dyn RequestResponseAuth>;
30}
31
32impl Clone for Box<dyn RequestResponseAuth> {
34 fn clone(&self) -> Self {
35 self.clone_box()
36 }
37}
38
39
40#[derive(Clone)]
41pub struct RgHttpClient {
42 pub url: String,
43 pub port: u16,
44 pub timeout: Duration,
45 pub http_proxy: Option<String>,
46 pub auth: Option<Box<dyn RequestResponseAuth>>
47}
48
49impl RgHttpClient {
50 pub async fn address_info_for_pk(&self, p0: &PublicKey) -> RgResult<AddressInfo> {
51 let mut req = Request::default();
52 req.get_address_info_public_key_request = Some(p0.clone());
53 let resp = self.proto_post_request(req, None, None).await?;
54 resp.get_address_info_public_key_response.ok_or(error_info("Missing get_address_info_response"))
55 }
56}
57
58impl RgHttpClient {
59 pub fn new(url: String, port: u16, signer: Option<Box<dyn RequestResponseAuth>>) -> Self {
60 Self {
61 url,
62 port,
63 timeout: Duration::from_secs(150),
64 http_proxy: None,
65 auth: signer,
66 }
67 }
68
69 pub fn with_http_proxy(mut self, http_proxy: String) -> Self {
70 self.http_proxy = Some(http_proxy);
71 self
72 }
73
74
75 pub async fn address_info(
76 &self,
77 address: Address,
78 ) -> Result<AddressInfo, ErrorInfo> {
79 let response = self.query_hash(address.render_string().expect("")).await?;
80 let ai = response.address_info.safe_get_msg("missing address_info")?;
81 Ok(ai.clone())
82 }
83
84 pub async fn send_transaction(
85 &self,
86 t: &Transaction,
87 sync: bool,
88 ) -> Result<SubmitTransactionResponse, ErrorInfo> {
89
90 let mut c = self.clone();
91 c.timeout = Duration::from_secs(180);
92
93 let mut request = Request::default();
94 request.submit_transaction_request = Some(SubmitTransactionRequest {
95 transaction: Some(t.clone()),
96 sync_query_response: sync,
97 });
98 debug!("Sending transaction: {}", t.clone().hash_hex());
99 let response = c.proto_post_request(request, None, None).await?;
100 response.as_error_info()?;
101 Ok(response.submit_transaction_response.safe_get()?.clone())
102 }
103
104
105
106 pub async fn balance(
107 &self,
108 address: &Address,
109 ) -> Result<i64, ErrorInfo> {
110 let response = self.query_hash(address.render_string().expect("")).await?;
111 let ai = response.address_info.safe_get_msg("missing address_info")?;
112 Ok(ai.balance)
113 }
114
115
116 pub fn url(&self) -> String {
117 format!("{}:{}", self.url, self.port)
118 }
119
120 pub fn from_env(url: String, network_environment: &NetworkEnvironment, signer: Option<Box<dyn RequestResponseAuth>>) -> Self {
121 Self {
122 url,
123 port: network_environment.default_port_offset() + 1,
124 timeout: Duration::from_secs(150),
125 http_proxy: None,
126 auth: signer,
127 }
128 }
129
130 #[allow(dead_code)]
131 fn formatted_url(&self) -> String {
132 return "http://".to_owned() + &*self.url.clone() + ":" + &*self.port.to_string();
133 }
134
135 fn metrics_url(&self) -> String {
136 format!("http://{}:{}/metrics", self.url, self.port - 2)
137 }
138
139 #[cfg(target_arch = "wasm32")]
140 pub async fn metrics(&self) -> RgResult<Vec<(String, String)>> {
141 "error".to_error()
142 }
143 #[cfg(not(target_arch = "wasm32"))]
144 pub async fn metrics(&self) -> RgResult<Vec<(String, String)>> {
145 let client = ClientBuilder::new().timeout(self.timeout).build().unwrap();
146 let sent = client
147 .get(self.metrics_url())
148 .send();
149 let response = sent.await.map_err(|e | error_info(e.to_string()))?;
150 let x = response.text().await;
151 let text = x.map_err(|e | error_info(e.to_string()))?;
152 let res = text.split("\n")
153 .filter(|x| !x.starts_with("#"))
154 .filter(|x| !x.trim().is_empty())
155 .map(|x| x.split(" "))
156 .map(|x| x.collect::<Vec<&str>>())
157 .flat_map(|x| x.get(0).as_ref().and_then(|k| x.get(1).as_ref().map(|v| (k.to_string(), v.to_string()))))
158 .collect::<Vec<(String, String)>>();
160 Ok(res)
161 }
162
163 pub async fn table_sizes(&self) -> RgResult<Vec<(String, i64)>> {
164 self.json_get("v1/tables").await
165 }
166
167 pub async fn explorer_public_address(&self, pk: &PublicKey) -> RgResult<Vec<DetailedAddress>> {
168 self.json_get(format!("v1/explorer/public/address/{}", pk.hex())).await
169 }
170
171 pub async fn table_sizes_map(&self) -> RgResult<HashMap<String, i64>> {
172 self.table_sizes().await.map(|v| v.into_iter().collect())
173 }
174 pub async fn metrics_map(&self) -> RgResult<HashMap<String, String>> {
175 self.metrics().await.map(|v| v.into_iter().collect())
176 }
177
178 #[cfg(not(target_arch = "wasm32"))]
179 #[allow(dead_code)]
180 pub async fn json_post<Req: Serialize + ?Sized, Resp: DeserializeOwned>(
181 &self,
182 r: &Req,
183 endpoint: String,
184 ) -> Result<Resp, ErrorInfo> {
185 self.client()?.post(format!("{}/{}", self.formatted_url(), endpoint))
186 .json::<Req>(r)
187 .send().await.error_info("error")?.text().await.error_info("error")?.json_from()
188 }
189
190 #[cfg(not(target_arch = "wasm32"))]
191 pub fn client(&self) -> RgResult<reqwest::Client> {
192 let mut builder = ClientBuilder::new().timeout(self.timeout);
193 if let Some(h) = self.http_proxy.as_ref() {
194 builder = builder.proxy(reqwest::Proxy::http(h).error_info("Failed to build proxy")?);
195 }
196 builder.build().error_info("Failed to build client")
197 }
198
199
200 #[cfg(target_arch = "wasm32")]
201 pub async fn json_get<Resp: DeserializeOwned>(
202 &self,
203 endpoint: impl Into<String>,
204 ) -> RgResult<Resp> {
205 "error".to_error()
206 }
207
208 #[cfg(not(target_arch = "wasm32"))]
209 pub async fn json_get<Resp: DeserializeOwned>(
210 &self,
211 endpoint: impl Into<String>,
212 ) -> RgResult<Resp> {
213 self.client()?
214 .get(format!("{}/{}", self.formatted_url(), endpoint.into()))
215 .send()
216 .await
217 .error_info("Failed to send get request")?
218 .text().await.error_info("Failed to get response text")?
219 .json_from::<Resp>()
220 }
221
222 #[cfg(target_arch = "wasm32")]
223 pub async fn proto_post<Req: Sized + ProtoSerde>(
224 &self,
225 r: &Req,
226 endpoint: String,
227 ) -> Result<Response, ErrorInfo> {
228 "not".to_error()
229 }
230
231 #[cfg(not(target_arch = "wasm32"))]
232 pub async fn proto_post<Req: Sized + ProtoSerde>(
233 &self,
234 r: &Req,
235 endpoint: String,
236 ) -> Result<Response, ErrorInfo> {
237 let sent = self.client()?
238 .post(format!("{}/{}", self.formatted_url(), endpoint))
239 .body(r.encode_to_vec())
240 .send();
241 let response = sent.await.map_err(|e| ErrorInfo::error_info(
242 format!("Proto request failure: {}", e.to_string())))
243 .with_detail("url", self.url.clone())
244 .with_detail("port", self.port.clone().to_string())?;
245 let bytes = response.bytes().await.map_err(|e| ErrorInfo::error_info(
246 format!("Proto request bytes decode failure: {}", e.to_string())))?;
247 let vec = bytes.to_vec();
248 let deser = Response::deserialize(vec).map_err(|e| ErrorInfo::error_info(
249 format!("Proto request response decode failure: {}", e.to_string())))?;
250 Ok(deser)
251 }
252
253
254 #[cfg(target_arch = "wasm32")]
255 pub async fn proto_post_request(
256 &self,
257 mut r: Request,
258 nmd: Option<NodeMetadata>,
259 intended_pk: Option<&PublicKey>
260 ) -> Result<Response, ErrorInfo> {
261 "not".to_error()
262 }
263 #[cfg(not(target_arch = "wasm32"))]
264 pub async fn proto_post_request(
265 &self,
266 mut r: Request,
267 nmd: Option<NodeMetadata>,
268 intended_pk: Option<&PublicKey>
269 ) -> Result<Response, ErrorInfo> {
270 let mut self2 = self.clone();
271 if let Some(ms) = &r.request_client_timeout_millis {
272 self2.timeout = Duration::from_millis(ms.clone() as u64);
273 }
274 if r.trace_id.is_none() {
275 r.trace_id = Some(Uuid::new_v4().to_string());
276 }
277
278 if let Some(nmd) = nmd {
279 r = r.with_metadata(nmd)
280 };
281 if let Some(signer) = self.auth.as_ref() {
282 r = signer.sign_request(&r)?;
283 }
284 let result = self2.proto_post(&r, "request_proto".to_string()).await?;
285 result.as_error_info().add("Response metadata found as errorInfo")?;
286 let string = result.json_or();
287 if let Some(signer) = self.auth.as_ref() {
288 signer.verify(result, intended_pk).add("Response authentication verification failure").add(string.with_max_length(1000))
289 } else {
290 Ok(result)
291 }
292 }
293
294 pub async fn get_peers(&self) -> Result<Response, ErrorInfo> {
295 let mut req = Request::default();
296 req.get_peers_info_request = Some(GetPeersInfoRequest::default());
297 let response = self.proto_post_request(req, None, None).await?;
298 Ok(response)
299 }
300
301 pub async fn contract_state(&self, address: &Address
302 ) -> RgResult<structs::ContractStateMarker> {
304 let mut req = Request::default();
305 let mut cmr = structs::GetContractStateMarkerRequest::default();
306 cmr.address = Some(address.clone());
308 req.get_contract_state_marker_request = Some(cmr);
309 let response = self.proto_post_request(req, None, None).await?;
310 Ok(response.get_contract_state_marker_response.ok_or(error_info("Missing get_contract_state_marker_response"))?)
311 }
312
313 pub async fn about(&self) -> RgResult<AboutNodeResponse> {
314 let mut req = Request::default();
315 req.about_node_request = Some(AboutNodeRequest::default());
316 let response = self.proto_post_request(req, None, None).await?;
317 Ok(response.about_node_response.ok_or(error_info("Missing about node response"))?)
318 }
319
320 pub async fn seeds(&self) -> RgResult<Vec<Seed>> {
321 let mut req = Request::default();
322 req.get_seeds_request = Some(structs::GetSeedsRequest::default());
323 let response = self.proto_post_request(req, None, None).await?;
324 Ok(response.get_seeds_response.clone())
325 }
326
327 pub async fn active_party_key(&self) -> RgResult<PublicKey> {
328 let mut req = Request::default();
329 req.get_active_party_key_request = Some(GetActivePartyKeyRequest::default());
330 let response = self.proto_post_request(req, None, None).await?;
331 Ok(response.get_active_party_key_response.ok_or(error_info("Missing get_active_party_key_response response"))?)
332 }
333
334 pub async fn balance_pk(&self, pk: &PublicKey) -> RgResult<CurrencyAmount> {
335 let mut req = Request::default();
336 req.get_public_key_balance_request = Some(pk.clone());
337 let response = self.proto_post_request(req, None, None).await?;
338 Ok(response.get_public_key_balance_response.ok_or(error_info("Missing get_public_key_balance_response response"))?)
339 }
340
341 #[cfg(not(target_arch = "wasm32"))]
342 pub async fn party_data(&self) -> RgResult<HashMap<PublicKey, PartyInternalData>> {
343 let pid = self.json_get::<Vec<PartyInternalData>>("v1/party/data").await?;
344 let mut hm = HashMap::new();
345 for pd in pid {
346 hm.insert(pd.proposer_key.clone(), pd);
347 }
348
349 Ok(hm)
350 }
351 #[cfg(not(target_arch = "wasm32"))]
352 pub async fn enriched_party_data(&self) -> HashMap<PublicKey, PartyInternalData> {
353 self.party_data().await.log_error().map(|mut r| {
354 r.iter_mut().for_each(|(_, v)| {
355 v.party_events.as_mut().map(|pev| {
356 pev.portfolio_request_events.enriched_events = Some(pev.portfolio_request_events.calculate_current_fulfillment_by_event());
357 });
358 });
359 r.clone()
360 }).unwrap_or_default()
361 }
362
363 pub async fn executable_checksum(&self) -> RgResult<String> {
364 let abt = self.about().await?;
365 let latest = abt.latest_node_metadata.safe_get_msg("Missing about node metadata latest node metadata")?;
366 let checksum = latest.node_metadata()?.version_info.map(|v| v.executable_checksum.clone());
367 checksum.safe_get_msg("Missing executable checksum").cloned()
368 }
369
370 pub async fn resolve_code(&self, address: &Address) -> RgResult<structs::ResolveCodeResponse> {
371 let mut req = Request::default();
372 req.resolve_code_request = Some(address.clone());
373 let response = self.proto_post_request(req, None, None).await?;
374 Ok(response.resolve_code_response.ok_or(error_info("Missing resolve code response"))?)
375 }
376
377 pub async fn genesis(&self) -> RgResult<Transaction> {
378 let mut req = Request::default();
379 req.genesis_request = Some(structs::GenesisRequest::default());
380 let response = self.proto_post_request(req, None, None).await?;
381 response.genesis_response.ok_msg("Missing genesis response")
382 }
383
384 #[allow(dead_code)]
385 pub async fn query_hash(
386 &self,
387 input: String,
388 ) -> Result<HashSearchResponse, ErrorInfo> {
389 let mut request = Request::default();
390 request.hash_search_request = Some(HashSearchRequest {
391 search_string: input
392 });
393 Ok(self.proto_post_request(request, None, None).await?.hash_search_response.safe_get()?.clone())
394 }
395
396}