1use std::collections::HashMap;
5
6use bytes::Bytes;
7use num_bigint::BigInt;
8use reqwest::Method;
9use serde::{Deserialize, Deserializer};
10
11use crate::client::request;
12use crate::swarm::Error;
13
14use super::DebugApi;
15
16#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
18pub struct Balance {
19 pub peer: String,
21 #[serde(deserialize_with = "deserialize_bigint")]
23 pub balance: BigInt,
24}
25
26fn deserialize_bigint<'de, D>(d: D) -> Result<BigInt, D::Error>
27where
28 D: Deserializer<'de>,
29{
30 let s: String = Deserialize::deserialize(d)?;
31 if s.is_empty() {
32 return Ok(BigInt::from(0));
33 }
34 s.parse::<BigInt>().map_err(serde::de::Error::custom)
35}
36
37fn deserialize_opt_bigint<'de, D>(d: D) -> Result<Option<BigInt>, D::Error>
38where
39 D: Deserializer<'de>,
40{
41 let s: String = Deserialize::deserialize(d)?;
42 if s.is_empty() {
43 return Ok(None);
44 }
45 s.parse::<BigInt>()
46 .map(Some)
47 .map_err(serde::de::Error::custom)
48}
49
50#[derive(Clone, Debug, PartialEq, Eq, Default, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct PeerAccounting {
55 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
57 pub balance: Option<BigInt>,
58 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
60 pub consumed_balance: Option<BigInt>,
61 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
63 pub threshold_received: Option<BigInt>,
64 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
66 pub threshold_given: Option<BigInt>,
67 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
69 pub current_threshold_received: Option<BigInt>,
70 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
72 pub current_threshold_given: Option<BigInt>,
73 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
75 pub surplus_balance: Option<BigInt>,
76 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
78 pub reserved_balance: Option<BigInt>,
79 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
81 pub shadow_reserved_balance: Option<BigInt>,
82 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
84 pub ghost_balance: Option<BigInt>,
85}
86
87#[derive(Clone, Debug, PartialEq, Default, Deserialize)]
90#[serde(rename_all = "camelCase")]
91pub struct RedistributionState {
92 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
94 pub minimum_gas_funds: Option<BigInt>,
95 pub has_sufficient_funds: bool,
97 pub is_frozen: bool,
99 pub is_fully_synced: bool,
101 pub phase: String,
103 pub round: u64,
105 pub last_won_round: u64,
107 pub last_played_round: u64,
109 pub last_frozen_round: u64,
111 pub last_selected_round: u64,
113 pub last_sample_duration_seconds: f64,
116 pub block: u64,
118 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
120 pub reward: Option<BigInt>,
121 #[serde(default, deserialize_with = "deserialize_opt_bigint")]
123 pub fees: Option<BigInt>,
124 pub is_healthy: bool,
126}
127
128#[derive(Clone, Debug, PartialEq, Default, Deserialize)]
132#[serde(rename_all = "camelCase")]
133pub struct RCHashResponse {
134 pub duration_seconds: f64,
137 pub hash: String,
139 #[serde(default)]
142 pub proofs: ChunkInclusionProofs,
143}
144
145#[derive(Clone, Debug, PartialEq, Default, Deserialize)]
147#[serde(rename_all = "camelCase")]
148pub struct ChunkInclusionProofs {
149 #[serde(default)]
151 pub proof1: ChunkInclusionProof,
152 #[serde(default)]
154 pub proof2: ChunkInclusionProof,
155 #[serde(default)]
157 pub proof_last: ChunkInclusionProof,
158}
159
160#[derive(Clone, Debug, PartialEq, Default, Deserialize)]
162#[serde(rename_all = "camelCase")]
163pub struct ChunkInclusionProof {
164 #[serde(default)]
166 pub chunk_span: u64,
167 #[serde(default)]
169 pub postage_proof: PostageProof,
170 pub proof_segments: Option<Vec<String>>,
172 pub proof_segments2: Option<Vec<String>>,
174 pub proof_segments3: Option<Vec<String>>,
176 #[serde(default)]
178 pub prove_segment: String,
179 #[serde(default)]
181 pub prove_segment2: String,
182 pub soc_proof: Option<Vec<SocProof>>,
184}
185
186#[derive(Clone, Debug, PartialEq, Default, Deserialize)]
188#[serde(rename_all = "camelCase")]
189pub struct PostageProof {
190 #[serde(default)]
192 pub index: String,
193 #[serde(default)]
195 pub postage_id: String,
196 #[serde(default)]
198 pub signature: String,
199 #[serde(default)]
201 pub time_stamp: String,
202}
203
204#[derive(Clone, Debug, PartialEq, Default, Deserialize)]
206#[serde(rename_all = "camelCase")]
207pub struct SocProof {
208 #[serde(default)]
210 pub chunk_addr: String,
211 #[serde(default)]
213 pub identifier: String,
214 #[serde(default)]
216 pub signature: String,
217 #[serde(default)]
219 pub signer: String,
220}
221
222impl DebugApi {
223 pub async fn balances(&self) -> Result<Vec<Balance>, Error> {
225 let builder = request(&self.inner, Method::GET, "balances")?;
226 #[derive(Deserialize)]
227 struct Resp {
228 balances: Vec<Balance>,
229 }
230 let r: Resp = self.inner.send_json(builder).await?;
231 Ok(r.balances)
232 }
233
234 pub async fn peer_balance(&self, address: &str) -> Result<Balance, Error> {
236 let path = format!("balances/{address}");
237 let builder = request(&self.inner, Method::GET, &path)?;
238 self.inner.send_json(builder).await
239 }
240
241 pub async fn consumed_balances(&self) -> Result<Vec<Balance>, Error> {
243 let builder = request(&self.inner, Method::GET, "consumed")?;
244 #[derive(Deserialize)]
245 struct Resp {
246 balances: Vec<Balance>,
247 }
248 let r: Resp = self.inner.send_json(builder).await?;
249 Ok(r.balances)
250 }
251
252 pub async fn peer_consumed_balance(&self, address: &str) -> Result<Balance, Error> {
255 let path = format!("consumed/{address}");
256 let builder = request(&self.inner, Method::GET, &path)?;
257 self.inner.send_json(builder).await
258 }
259
260 pub async fn accounting(&self) -> Result<HashMap<String, PeerAccounting>, Error> {
263 let builder = request(&self.inner, Method::GET, "accounting")?;
264 #[derive(Deserialize)]
265 struct Resp {
266 #[serde(rename = "peerData")]
267 peer_data: HashMap<String, PeerAccounting>,
268 }
269 let r: Resp = self.inner.send_json(builder).await?;
270 Ok(r.peer_data)
271 }
272
273 pub async fn stake(&self) -> Result<BigInt, Error> {
277 let builder = request(&self.inner, Method::GET, "stake")?;
278 #[derive(Deserialize)]
279 struct Resp {
280 #[serde(rename = "stakedAmount")]
281 staked_amount: String,
282 }
283 let r: Resp = self.inner.send_json(builder).await?;
284 r.staked_amount.parse::<BigInt>().map_err(|e| {
285 Error::argument(format!("invalid stakedAmount {:?}: {e}", r.staked_amount))
286 })
287 }
288
289 pub async fn deposit_stake(&self, amount: &BigInt) -> Result<String, Error> {
292 let path = format!("stake/{amount}");
293 let builder = request(&self.inner, Method::POST, &path)?;
294 tx_hash_response(&self.inner, builder).await
295 }
296
297 pub async fn withdrawable_stake(&self) -> Result<BigInt, Error> {
299 let builder = request(&self.inner, Method::GET, "stake/withdrawable")?;
300 #[derive(Deserialize)]
301 struct Resp {
302 #[serde(rename = "withdrawableAmount")]
303 withdrawable_amount: String,
304 }
305 let r: Resp = self.inner.send_json(builder).await?;
306 r.withdrawable_amount.parse::<BigInt>().map_err(|e| {
307 Error::argument(format!(
308 "invalid withdrawableAmount {:?}: {e}",
309 r.withdrawable_amount
310 ))
311 })
312 }
313
314 pub async fn withdraw_surplus_stake(&self) -> Result<String, Error> {
316 let builder = request(&self.inner, Method::DELETE, "stake/withdrawable")?;
317 tx_hash_response(&self.inner, builder).await
318 }
319
320 pub async fn migrate_stake(&self) -> Result<String, Error> {
323 let builder = request(&self.inner, Method::DELETE, "stake")?;
324 tx_hash_response(&self.inner, builder).await
325 }
326
327 pub async fn redistribution_state(&self) -> Result<RedistributionState, Error> {
329 let builder = request(&self.inner, Method::GET, "redistributionstate")?;
330 self.inner.send_json(builder).await
331 }
332
333 pub async fn r_chash(
340 &self,
341 depth: u8,
342 anchor1: &str,
343 anchor2: &str,
344 ) -> Result<RCHashResponse, Error> {
345 let path = format!("rchash/{depth}/{anchor1}/{anchor2}");
346 let builder = request(&self.inner, Method::GET, &path)?;
347 self.inner.send_json(builder).await
348 }
349}
350
351async fn tx_hash_response(
352 inner: &crate::client::Inner,
353 builder: reqwest::RequestBuilder,
354) -> Result<String, Error> {
355 #[derive(Deserialize)]
356 struct Resp {
357 #[serde(rename = "txHash")]
358 tx_hash: String,
359 }
360 let resp = inner.send(builder).await?;
361 let bytes: Bytes = resp.bytes().await?;
362 let r: Resp = serde_json::from_slice(&bytes)?;
363 Ok(r.tx_hash)
364}