Skip to main content

arweave_rs/
network.rs

1use crate::{
2    client::Client,
3    types::{BlockInfo, NetworkInfo},
4};
5use pretend::{
6    interceptor::NoopRequestInterceptor, pretend, resolver::UrlResolver, JsonResult, Pretend, Url,
7};
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11#[derive(Serialize, Deserialize, Debug)]
12struct HeightInfo {
13    height: u64,
14}
15
16#[derive(Debug, Error, Deserialize)]
17
18pub enum ResponseError {
19    #[error("Internal error")]
20    InternalError(String),
21
22    #[error("Unknown error")]
23    UnknownError(String),
24}
25
26#[pretend]
27trait NetworkInfoFetch {
28    #[request(method = "GET", path = "/info")]
29    async fn network_info(&self) -> pretend::Result<JsonResult<NetworkInfo, ResponseError>>;
30
31    #[request(method = "GET", path = "/peers")]
32    async fn peer_info(&self) -> pretend::Result<JsonResult<Vec<String>, ResponseError>>;
33
34    #[request(method = "GET", path = "/block/hash/{id}")]
35    async fn block_by_hash(
36        &self,
37        id: &str,
38    ) -> pretend::Result<JsonResult<BlockInfo, ResponseError>>;
39
40    #[request(method = "GET", path = "/block/height/{height}")]
41    async fn block_by_height(
42        &self,
43        height: u64,
44    ) -> pretend::Result<JsonResult<BlockInfo, ResponseError>>;
45}
46
47pub struct NetworkInfoClient(Pretend<Client, UrlResolver, NoopRequestInterceptor>);
48
49impl NetworkInfoClient {
50    pub fn new(url: Url) -> Self {
51        let client = Client::default();
52        let pretend = Pretend::for_client(client).with_url(url);
53        Self(pretend)
54    }
55
56    pub async fn network_info(&self) -> Result<NetworkInfo, ResponseError> {
57        let response = self
58            .0
59            .network_info()
60            .await
61            .map_err(|err| ResponseError::InternalError(err.to_string()))?;
62        match response {
63            JsonResult::Ok(n) => Ok(n),
64            JsonResult::Err(err) => Err(err),
65        }
66    }
67
68    pub async fn peer_info(&self) -> Result<Vec<String>, ResponseError> {
69        let response = self
70            .0
71            .peer_info()
72            .await
73            .map_err(|err| ResponseError::InternalError(err.to_string()))?;
74        match response {
75            JsonResult::Ok(n) => Ok(n),
76            JsonResult::Err(err) => Err(err),
77        }
78    }
79
80    pub async fn block_by_hash(&self, id: &str) -> Result<BlockInfo, ResponseError> {
81        let response = self
82            .0
83            .block_by_hash(id)
84            .await
85            .map_err(|err| ResponseError::InternalError(err.to_string()))?;
86        match response {
87            JsonResult::Ok(n) => Ok(n),
88            JsonResult::Err(err) => Err(err),
89        }
90    }
91
92    pub async fn block_by_height(&self, id: &str) -> Result<BlockInfo, ResponseError> {
93        let response = self
94            .0
95            .block_by_hash(id)
96            .await
97            .map_err(|err| ResponseError::InternalError(err.to_string()))?;
98        match response {
99            JsonResult::Ok(n) => Ok(n),
100            JsonResult::Err(err) => Err(err),
101        }
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use std::str::FromStr;
108
109    use crate::{consts::ARWEAVE_BASE_URL, crypto::base64::Base64, network::NetworkInfoClient};
110    use pretend::Url;
111    use tokio_test::block_on;
112
113    #[test]
114    fn test_network_info() {
115        let url = Url::parse(ARWEAVE_BASE_URL).unwrap();
116        let client = NetworkInfoClient::new(url);
117        let network_info = block_on(client.network_info()).unwrap();
118
119        assert_eq!(network_info.network, "arweave.N.1".to_string());
120    }
121
122    #[test]
123    fn test_peer_info() {
124        let url = Url::parse(ARWEAVE_BASE_URL).unwrap();
125        let client = NetworkInfoClient::new(url);
126        let peer_info = block_on(client.peer_info()).unwrap();
127
128        assert!(!peer_info.is_empty());
129    }
130
131    #[test]
132    fn test_block_info() {
133        let url = Url::parse(ARWEAVE_BASE_URL).unwrap();
134        let client = NetworkInfoClient::new(url);
135
136        let block_hash_v1 = "ngFDAB2KRhJgJRysuhpp1u65FjBf5WZk99_NyoMx8w6uP0IVjzb93EVkYxmcErdZ";
137        let block_info_v1 = block_on(client.block_by_hash(block_hash_v1)).unwrap();
138        assert_eq!(block_info_v1.nonce, Base64::from_str("AAEBAAABAQAAAQAAAQEBAAEAAAABAQABAQABAAEAAAEBAAAAAQAAAAAAAQAAAQEBAAEBAAEBAQEBAQEAAQEBAAABAQEAAQAAAQABAAABAAAAAAEBAQEBAAABAQEAAAAAAAABAQAAAQAAAQEAAQABAQABAQEAAAABAAABAQABAQEAAAEBAQABAQEBAQEBAAABAQEAAAABAQABAAABAAEAAQEBAQAAAAABAQABAQAAAAAAAAABAQABAAEBAAEAAQABAQABAAEBAQEBAAEAAQABAAABAQEBAQAAAQABAQEBAAEBAQAAAQEBAQABAAEBAQEBAAAAAAABAAEAAAEAAAEAAAEBAAAAAAEAAQABAAAAAAABAQABAQAAAAEBAQAAAAABAAABAAEBAQEAAAAAAQAAAQABAQABAAEAAQABAQAAAAEBAQAAAQAAAAEBAAEBAAEBAQEAAAEBAQAAAQAAAAABAAEAAQEAAQ").unwrap());
139        assert_eq!(
140            block_info_v1.previous_block,
141            Base64::from_str("V6YjG8G3he0JIIwRtzTccX39rS0jH-jOqUJy6rxrVAHY0RT0AVhG8K22wCDxy1A0")
142                .unwrap()
143        );
144        assert_eq!(block_info_v1.timestamp, 1528500720);
145        assert_eq!(block_info_v1.last_retarget, 1528500720);
146        assert_eq!(block_info_v1.diff, "31");
147        assert_eq!(block_info_v1.height, 100);
148        assert_eq!(
149            block_info_v1.hash,
150            Base64::from_str("AAAAANsEvzGbICpfAj3NN41_ox--2cNxkEhAo0aggpDPkY7zru29g24uMWUP9hTa")
151                .unwrap()
152        );
153        assert_eq!(
154            block_info_v1.indep_hash,
155            Base64::from_str(block_hash_v1).unwrap()
156        );
157        assert_eq!(block_info_v1.txs.len(), 1);
158        assert_eq!(block_info_v1.tx_root, Base64::default());
159        assert_eq!(block_info_v1.tx_tree.len(), 0);
160        assert_eq!(
161            block_info_v1.wallet_list,
162            Base64::from_str("ph2FDDuQjNbca34tz7vP9X5Xve2EGJi2ZgFqhMITAdw").unwrap()
163        );
164        assert_eq!(
165            block_info_v1.reward_addr,
166            Base64::from_str("em8MfGRInwWEAQnE6b50ENaFOf-0to4Pbygng1ilWGQ").unwrap()
167        );
168        assert_eq!(block_info_v1.tags, []);
169        assert_eq!(block_info_v1.reward_pool, 60770606104);
170        assert_eq!(block_info_v1.weave_size, 599058);
171        assert_eq!(block_info_v1.block_size, 0);
172        assert_eq!(block_info_v1.poa.option, "1");
173        assert_eq!(block_info_v1.poa.tx_path, Base64::default());
174        assert_eq!(block_info_v1.poa.data_path, Base64::default());
175        assert_eq!(block_info_v1.poa.chunk, Base64::default());
176
177        let block_hash_v2 = "5H-hJycMS_PnPOpobXu2CNobRlgqmw4yEMQSc5LeBfS7We63l8HjS-Ek3QaxK8ug";
178        let block_info_v2 = block_on(client.block_by_hash(block_hash_v2)).unwrap();
179        assert_eq!(
180            block_info_v2.nonce,
181            Base64::from_str("O3IQWXYmxLN_b0w7QyT2GTruaVIGsl-Ybhc6Pl2V20U").unwrap()
182        );
183        assert_eq!(
184            block_info_v2.previous_block,
185            Base64::from_str("VRVYubqppWUVAeCWlzHR-38dQoWcFAKbGculkVZThfj-hNMX4QVZjqkC6-PkiNGE")
186                .unwrap()
187        );
188        assert_eq!(block_info_v2.timestamp, 1567052949);
189        assert_eq!(block_info_v2.last_retarget, 1567052114);
190        assert_eq!(
191            block_info_v2.diff,
192            "115792088374597902074750511579343425068641803109251942518159264612597601665024"
193        );
194        assert_eq!(block_info_v2.height, 269512);
195        assert_eq!(
196            block_info_v2.hash,
197            Base64::from_str("____47liyh_OZdYUP4EzBoLl7JOPge9VsWPQ3b5kiU8").unwrap()
198        );
199        assert_eq!(
200            block_info_v2.indep_hash,
201            Base64::from_str(block_hash_v2).unwrap()
202        );
203        assert_eq!(block_info_v2.txs.len(), 2);
204        assert_eq!(block_info_v2.tx_root, Base64::default());
205        assert_eq!(block_info_v2.tx_tree.len(), 0);
206        assert_eq!(
207            block_info_v2.wallet_list,
208            Base64::from_str("6haahtRP5WVchxPbqtLCqDsFWidhebYJpU5PVB4zQhE").unwrap()
209        );
210        assert_eq!(
211            block_info_v2.reward_addr,
212            Base64::from_str("aE1AjkBoXBfF-PRP2dzRrbYY8cY2OYzeH551nSPRU5M").unwrap()
213        );
214        assert_eq!(block_info_v2.tags, []);
215        assert_eq!(block_info_v2.reward_pool, 0);
216        assert_eq!(block_info_v2.weave_size, 21080508475);
217        assert_eq!(block_info_v2.block_size, 991723);
218        assert!(block_info_v2.cumulative_diff.is_some());
219        assert_eq!(block_info_v2.cumulative_diff.unwrap(), "616416144");
220        assert!(block_info_v2.hash_list_merkle.is_some());
221        assert_eq!(
222            block_info_v2.hash_list_merkle.unwrap(),
223            Base64::from_str("1QVbbLwZHpNMJd8ZghRb13HZfrRu-aIIfzY29r64_yBJAcYv-Kfblv_c2pfKbQBP")
224                .unwrap()
225        );
226        assert_eq!(block_info_v2.poa.option, "1");
227        assert_eq!(block_info_v2.poa.tx_path, Base64::default());
228        assert_eq!(block_info_v2.poa.data_path, Base64::default());
229        assert_eq!(block_info_v2.poa.chunk, Base64::default());
230
231        let block_hash_v3 = "5VTARz7bwDO4GqviCSI9JXm8_JOtoQwF-QCZm0Gt2gVgwdzSY3brOtOD46bjMz09";
232        let block_info_v3 = block_on(client.block_by_hash(block_hash_v3)).unwrap();
233        assert_eq!(
234            block_info_v3.nonce,
235            Base64::from_str("W3Jy4wp2LVbDFhGX_hUjRQZCkTdEbKxz45E5OVe52Lo").unwrap()
236        );
237        assert_eq!(
238            block_info_v3.previous_block,
239            Base64::from_str("YuTyalVBTNB9t5KhuRezcIgxVz9PbQsbrcY4Tpkiu8XBPgglGM_Yql5qZd0c9PVG")
240                .unwrap()
241        );
242        assert_eq!(block_info_v3.timestamp, 1586440919);
243        assert_eq!(block_info_v3.last_retarget, 1586440919);
244        assert_eq!(
245            block_info_v3.diff,
246            "115792089039110416381168389782714091630053560834545856346499935466490404274176"
247        );
248        assert_eq!(block_info_v3.height, 422250);
249        assert_eq!(
250            block_info_v3.hash,
251            Base64::from_str("_____8422fLZnBsEsxtwEdpi8GZDHVT-aFlqroQDG44").unwrap()
252        );
253        assert_eq!(
254            block_info_v3.indep_hash,
255            Base64::from_str(block_hash_v3).unwrap()
256        );
257        assert_eq!(block_info_v3.txs.len(), 1);
258        assert_eq!(
259            block_info_v3.tx_root,
260            Base64::from_str("lsoo-p3Tj7oblZ-54WVPHoVguqgw5rA9Jf3lLH6H8zY").unwrap()
261        );
262        assert_eq!(block_info_v3.tx_tree.len(), 0);
263        assert_eq!(
264            block_info_v3.wallet_list,
265            Base64::from_str("N5NJtXhgH9bPmXoSopehcr_zqwyPjjg3igel0V8G1DdLk_BYdoRVIBsqjVA9JmFc")
266                .unwrap()
267        );
268        assert_eq!(
269            block_info_v3.reward_addr,
270            Base64::from_str("Oox7m4HIcVhUtMd6AUuGtlaOoSCmREUNPyyKQCbz4d4").unwrap()
271        );
272        assert_eq!(block_info_v3.tags, []);
273        assert_eq!(block_info_v3.reward_pool, 3026104059201252);
274        assert_eq!(block_info_v3.weave_size, 407672420044);
275        assert_eq!(block_info_v3.block_size, 937455);
276        assert!(block_info_v3.cumulative_diff.is_some());
277        assert_eq!(block_info_v3.cumulative_diff.unwrap(), "99416580392277");
278        assert!(block_info_v3.hash_list_merkle.is_some());
279        assert_eq!(
280            block_info_v3.hash_list_merkle.unwrap(),
281            Base64::from_str("akSjDrBKPuepJMOhO_S9C-iFp5zn9Glv57HGdN_WPqEToWC0Ukb37Gzs4PDA7oLU")
282                .unwrap()
283        );
284        assert_eq!(block_info_v3.poa.option, "1");
285        assert_eq!(block_info_v3.poa.tx_path.0.len(), 640);
286        assert_eq!(block_info_v3.poa.data_path.0.len(), 352);
287        assert_eq!(block_info_v3.poa.chunk.0.len(), 262144);
288    }
289}