1use std::{
3 collections::HashMap,
4 io::{self, Error, ErrorKind},
5 net::{IpAddr, Ipv4Addr, SocketAddr},
6};
7
8use crate::{
9 ids::{self, node},
10 jsonrpc,
11 key::bls,
12};
13use chrono::{DateTime, Utc};
14use serde::{Deserialize, Serialize};
15use serde_with::{serde_as, DisplayFromStr};
16
17#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
19pub struct GetNetworkNameResponse {
20 pub jsonrpc: String,
21 pub id: u32,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub result: Option<GetNetworkNameResult>,
25
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub error: Option<jsonrpc::ResponseError>,
28}
29
30#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
32#[serde(rename_all = "camelCase")]
33#[derive(Default)]
34pub struct GetNetworkNameResult {
35 pub network_name: String,
36}
37
38#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
40pub struct GetNetworkIdResponse {
41 pub jsonrpc: String,
42 pub id: u32,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub result: Option<GetNetworkIdResult>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub error: Option<jsonrpc::ResponseError>,
49}
50
51#[serde_as]
53#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
54pub struct GetNetworkIdResult {
55 #[serde(rename = "networkID")]
56 #[serde_as(as = "DisplayFromStr")]
57 pub network_id: u32,
58}
59
60impl Default for GetNetworkIdResult {
61 fn default() -> Self {
62 Self { network_id: 1 }
63 }
64}
65
66#[test]
68fn test_get_network_id() {
69 let resp: GetNetworkIdResponse = serde_json::from_str(
71 "
72
73{
74 \"jsonrpc\": \"2.0\",
75 \"result\": {
76 \"networkID\": \"9999999\"
77 },
78 \"id\": 1
79}
80
81",
82 )
83 .unwrap();
84
85 let expected = GetNetworkIdResponse {
86 jsonrpc: "2.0".to_string(),
87 id: 1,
88 result: Some(GetNetworkIdResult {
89 network_id: 9999999_u32,
90 }),
91 error: None,
92 };
93 assert_eq!(resp, expected);
94}
95
96#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
98pub struct GetBlockchainIdResponse {
99 pub jsonrpc: String,
100 pub id: u32,
101
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub result: Option<GetBlockchainIdResult>,
104
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub error: Option<jsonrpc::ResponseError>,
107}
108
109#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
111pub struct GetBlockchainIdResult {
112 #[serde(rename = "blockchainID")]
113 pub blockchain_id: ids::Id,
114}
115
116#[test]
118fn test_get_blockchain_id() {
119 use std::str::FromStr;
120
121 let resp: GetBlockchainIdResponse = serde_json::from_str(
123 "
124
125{
126 \"jsonrpc\": \"2.0\",
127 \"result\": {
128 \"blockchainID\": \"sV6o671RtkGBcno1FiaDbVcFv2sG5aVXMZYzKdP4VQAWmJQnM\"
129 },
130 \"id\": 1
131}
132
133",
134 )
135 .unwrap();
136
137 let expected = GetBlockchainIdResponse {
138 jsonrpc: "2.0".to_string(),
139 id: 1,
140 result: Some(GetBlockchainIdResult {
141 blockchain_id: ids::Id::from_str("sV6o671RtkGBcno1FiaDbVcFv2sG5aVXMZYzKdP4VQAWmJQnM")
142 .unwrap(),
143 }),
144 error: None,
145 };
146 assert_eq!(resp, expected);
147}
148
149#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
151pub struct GetNodeIdResponse {
152 pub jsonrpc: String,
153 pub id: u32,
154
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub result: Option<GetNodeIdResult>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub error: Option<jsonrpc::ResponseError>,
160}
161
162#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
164pub struct GetNodeIdResult {
165 #[serde(rename = "nodeID")]
166 pub node_id: node::Id,
167 #[serde(rename = "nodePOP")]
168 pub node_pop: Option<bls::ProofOfPossession>,
169}
170
171#[test]
173fn test_get_node_id() {
174 use std::str::FromStr;
175
176 let resp: GetNodeIdResponse = serde_json::from_str(
178 "
179
180{
181 \"jsonrpc\": \"2.0\",
182 \"result\": {
183 \"nodeID\": \"NodeID-5mb46qkSBj81k9g9e4VFjGGSbaaSLFRzD\"
184 },
185 \"id\": 1
186}
187
188",
189 )
190 .unwrap();
191 let expected = GetNodeIdResponse {
192 jsonrpc: "2.0".to_string(),
193 id: 1,
194 result: Some(GetNodeIdResult {
195 node_id: node::Id::from_str("NodeID-5mb46qkSBj81k9g9e4VFjGGSbaaSLFRzD").unwrap(),
196 ..Default::default()
197 }),
198 error: None,
199 };
200 assert_eq!(resp, expected);
201
202 let resp: GetNodeIdResponse = serde_json::from_str(
203 "
204
205{
206 \"jsonrpc\": \"2.0\",
207 \"result\": {
208 \"nodeID\": \"NodeID-5mb46qkSBj81k9g9e4VFjGGSbaaSLFRzD\",
209 \"nodePOP\": {
210 \"publicKey\": \"0x8f95423f7142d00a48e1014a3de8d28907d420dc33b3052a6dee03a3f2941a393c2351e354704ca66a3fc29870282e15\",
211 \"proofOfPossession\": \"0x86a3ab4c45cfe31cae34c1d06f212434ac71b1be6cfe046c80c162e057614a94a5bc9f1ded1a7029deb0ba4ca7c9b71411e293438691be79c2dbf19d1ca7c3eadb9c756246fc5de5b7b89511c7d7302ae051d9e03d7991138299b5ed6a570a98\"
212 }
213 },
214 \"id\": 1
215}
216
217",
218 )
219 .unwrap();
220 let expected = GetNodeIdResponse {
221 jsonrpc: "2.0".to_string(),
222 id: 1,
223 result: Some(GetNodeIdResult {
224 node_id: node::Id::from_str("NodeID-5mb46qkSBj81k9g9e4VFjGGSbaaSLFRzD").unwrap(),
225 node_pop: Some(bls::ProofOfPossession {
226 public_key: hex::decode("0x8f95423f7142d00a48e1014a3de8d28907d420dc33b3052a6dee03a3f2941a393c2351e354704ca66a3fc29870282e15".trim_start_matches("0x")).unwrap(),
227 proof_of_possession: hex::decode("0x86a3ab4c45cfe31cae34c1d06f212434ac71b1be6cfe046c80c162e057614a94a5bc9f1ded1a7029deb0ba4ca7c9b71411e293438691be79c2dbf19d1ca7c3eadb9c756246fc5de5b7b89511c7d7302ae051d9e03d7991138299b5ed6a570a98".trim_start_matches("0x")).unwrap(),
228 ..Default::default()
229 }),
230 }),
231 error: None,
232 };
233 assert_eq!(resp, expected);
234}
235
236#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
238pub struct GetNodeIpResponse {
239 pub jsonrpc: String,
240 pub id: u32,
241
242 #[serde(skip_serializing_if = "Option::is_none")]
243 pub result: Option<GetNodeIpResult>,
244
245 #[serde(skip_serializing_if = "Option::is_none")]
246 pub error: Option<jsonrpc::ResponseError>,
247}
248
249#[serde_as]
251#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
252pub struct GetNodeIpResult {
253 #[serde_as(as = "crate::codec::serde::ip_port::IpPort")]
254 pub ip: SocketAddr,
255}
256
257impl Default for GetNodeIpResult {
258 fn default() -> Self {
259 Self {
260 ip: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9651),
261 }
262 }
263}
264#[test]
266fn test_get_node_ip() {
267 let resp: GetNodeIpResponse = serde_json::from_str(
269 "
270
271{
272 \"jsonrpc\": \"2.0\",
273 \"result\": {
274 \"ip\": \"192.168.1.1:9651\"
275 },
276 \"id\": 1
277}
278
279",
280 )
281 .unwrap();
282 let expected = GetNodeIpResponse {
283 jsonrpc: "2.0".to_string(),
284 id: 1,
285 result: Some(GetNodeIpResult {
286 ip: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 9651),
287 }),
288 error: None,
289 };
290 assert_eq!(resp, expected);
291}
292
293#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
295pub struct GetNodeVersionResponse {
296 pub jsonrpc: String,
297 pub id: u32,
298
299 #[serde(skip_serializing_if = "Option::is_none")]
300 pub result: Option<GetNodeVersionResult>,
301
302 #[serde(skip_serializing_if = "Option::is_none")]
303 pub error: Option<jsonrpc::ResponseError>,
304}
305
306#[derive(Default, Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
308#[serde(rename_all = "camelCase")]
309pub struct GetNodeVersionResult {
310 pub version: String,
311 pub database_version: String,
312 pub git_commit: String,
313 pub vm_versions: VmVersions,
314 pub rpc_protocol_version: String,
315}
316
317#[derive(Default, Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
319#[serde(rename_all = "camelCase")]
320pub struct VmVersions {
321 pub avm: String,
322 pub evm: String,
323 pub platform: String,
324 #[serde(flatten)]
325 pub subnets: HashMap<String, String>,
326}
327
328#[test]
330fn test_get_node_version() {
331 let resp: GetNodeVersionResponse = serde_json::from_str(
332 r#"
333{
334 "jsonrpc": "2.0",
335 "result": {
336 "version": "avalanche/1.10.1",
337 "databaseVersion": "v1.4.5",
338 "rpcProtocolVersion": "26",
339 "gitCommit": "ef6a2a2f7facd8fbefd5fb2ac9c4908c2bcae3e2",
340 "vmVersions": {
341 "avm": "v1.10.1",
342 "evm": "v0.12.1",
343 "platform": "v1.10.1",
344 "subnet-evm": "v0.5.1"
345 }
346 },
347 "id": 1
348}
349"#,
350 )
351 .unwrap();
352 let expected = GetNodeVersionResponse {
353 jsonrpc: "2.0".to_string(),
354 id: 1,
355 result: Some(GetNodeVersionResult {
356 version: String::from("avalanche/1.10.1"),
357 database_version: String::from("v1.4.5"),
358 git_commit: String::from("ef6a2a2f7facd8fbefd5fb2ac9c4908c2bcae3e2"),
359 vm_versions: VmVersions {
360 avm: String::from("v1.10.1"),
361 evm: String::from("v0.12.1"),
362 platform: String::from("v1.10.1"),
363 subnets: HashMap::from([(String::from("subnet-evm"), String::from("v0.5.1"))]),
364 },
365 rpc_protocol_version: String::from("26"),
366 }),
367 error: None,
368 };
369 assert_eq!(resp, expected);
370}
371
372#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
374pub struct GetVmsResponse {
375 pub jsonrpc: String,
376 pub id: u32,
377
378 #[serde(skip_serializing_if = "Option::is_none")]
379 pub result: Option<GetVmsResult>,
380
381 #[serde(skip_serializing_if = "Option::is_none")]
382 pub error: Option<jsonrpc::ResponseError>,
383}
384
385#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
387#[serde(rename_all = "camelCase")]
388#[derive(Default)]
389pub struct GetVmsResult {
390 #[serde(skip_serializing_if = "Option::is_none")]
391 pub vms: Option<HashMap<String, Vec<String>>>,
392}
393
394#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
396pub struct IsBootstrappedResponse {
397 pub jsonrpc: String,
398 pub id: u32,
399
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub result: Option<IsBootstrappedResult>,
402
403 #[serde(skip_serializing_if = "Option::is_none")]
404 pub error: Option<jsonrpc::ResponseError>,
405}
406
407#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
409#[serde(rename_all = "camelCase")]
410#[derive(Default)]
411pub struct IsBootstrappedResult {
412 pub is_bootstrapped: bool,
413}
414
415#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
417pub struct GetTxFeeResponse {
418 pub jsonrpc: String,
419 pub id: u32,
420
421 #[serde(skip_serializing_if = "Option::is_none")]
422 pub result: Option<GetTxFeeResult>,
423
424 #[serde(skip_serializing_if = "Option::is_none")]
425 pub error: Option<jsonrpc::ResponseError>,
426}
427
428#[serde_as]
430#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
431#[serde(rename_all = "camelCase")]
432#[derive(Default)]
433pub struct GetTxFeeResult {
434 #[serde_as(as = "DisplayFromStr")]
435 pub tx_fee: u64,
436 #[serde_as(as = "DisplayFromStr")]
437 pub create_asset_tx_fee: u64,
438 #[serde_as(as = "DisplayFromStr")]
439 pub create_subnet_tx_fee: u64,
440 #[serde_as(as = "DisplayFromStr")]
441 pub transform_subnet_tx_fee: u64,
442 #[serde_as(as = "DisplayFromStr")]
443 pub create_blockchain_tx_fee: u64,
444 #[serde_as(as = "DisplayFromStr")]
445 pub add_primary_network_validator_fee: u64,
446 #[serde_as(as = "DisplayFromStr")]
447 pub add_primary_network_delegator_fee: u64,
448 #[serde_as(as = "DisplayFromStr")]
449 pub add_subnet_validator_fee: u64,
450 #[serde_as(as = "DisplayFromStr")]
451 pub add_subnet_delegator_fee: u64,
452}
453
454#[test]
456fn test_get_tx_fee() {
457 let resp: GetTxFeeResponse = serde_json::from_str(
460 "
461
462{
463 \"jsonrpc\": \"2.0\",
464 \"result\": {
465 \"txFee\": \"1000000\",
466 \"createAssetTxFee\": \"1000000\",
467 \"createSubnetTxFee\": \"100000000\",
468 \"transformSubnetTxFee\": \"100000000\",
469 \"createBlockchainTxFee\": \"100000000\",
470 \"addPrimaryNetworkValidatorFee\": \"0\",
471 \"addPrimaryNetworkDelegatorFee\": \"1000000\",
472 \"addSubnetValidatorFee\": \"1000000\",
473 \"addSubnetDelegatorFee\": \"1000000\"
474 },
475 \"id\": 1
476}
477
478",
479 )
480 .unwrap();
481
482 let expected = GetTxFeeResponse {
483 jsonrpc: "2.0".to_string(),
484 id: 1,
485 result: Some(GetTxFeeResult {
486 tx_fee: 1000000,
487 create_asset_tx_fee: 1000000,
488 create_subnet_tx_fee: 100000000,
489 transform_subnet_tx_fee: 100000000,
490 create_blockchain_tx_fee: 100000000,
491 add_primary_network_validator_fee: 0,
492 add_primary_network_delegator_fee: 1000000,
493 add_subnet_validator_fee: 1000000,
494 add_subnet_delegator_fee: 1000000,
495 }),
496 error: None,
497 };
498 assert_eq!(resp, expected);
499}
500
501#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
503pub struct UptimeResponse {
504 pub jsonrpc: String,
505 pub id: u32,
506
507 #[serde(skip_serializing_if = "Option::is_none")]
508 pub result: Option<UptimeResult>,
509
510 #[serde(skip_serializing_if = "Option::is_none")]
511 pub error: Option<jsonrpc::ResponseError>,
512}
513
514#[serde_as]
516#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
517#[serde(rename_all = "camelCase")]
518pub struct UptimeResult {
519 #[serde_as(as = "DisplayFromStr")]
520 pub rewarding_stake_percentage: f64,
521 #[serde_as(as = "DisplayFromStr")]
522 pub weighted_average_percentage: f64,
523}
524
525impl Default for UptimeResult {
526 fn default() -> Self {
527 Self {
528 rewarding_stake_percentage: 0_f64,
529 weighted_average_percentage: 0_f64,
530 }
531 }
532}
533
534#[test]
536fn test_uptime() {
537 let resp: UptimeResponse = serde_json::from_str(
539 "
540
541{
542 \"jsonrpc\": \"2.0\",
543 \"result\": {
544 \"rewardingStakePercentage\": \"100.0000\",
545 \"weightedAveragePercentage\": \"99.0000\"
546 },
547 \"id\": 1
548}
549
550",
551 )
552 .unwrap();
553
554 let expected = UptimeResponse {
555 jsonrpc: "2.0".to_string(),
556 id: 1,
557 result: Some(UptimeResult {
558 rewarding_stake_percentage: 100.0000_f64,
559 weighted_average_percentage: 99.0000_f64,
560 }),
561 error: None,
562 };
563 assert_eq!(resp, expected);
564}
565
566#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
568pub struct PeersRequest {
569 pub jsonrpc: String,
570 pub id: u32,
571
572 pub method: String,
573
574 #[serde(skip_serializing_if = "Option::is_none")]
575 pub params: Option<PeersParams>,
576}
577
578impl Default for PeersRequest {
579 fn default() -> Self {
580 Self {
581 jsonrpc: String::from(super::DEFAULT_VERSION),
582 id: super::DEFAULT_ID,
583 method: String::new(),
584 params: None,
585 }
586 }
587}
588
589impl PeersRequest {
590 pub fn encode_json(&self) -> io::Result<String> {
591 serde_json::to_string(&self)
592 .map_err(|e| Error::new(ErrorKind::Other, format!("failed to serialize JSON {}", e)))
593 }
594}
595
596#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
597pub struct PeersParams {
598 #[serde(rename = "nodeIDs")]
599 pub node_ids: Option<Vec<ids::node::Id>>,
600}
601
602#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
604pub struct PeersResponse {
605 pub jsonrpc: String,
606 pub id: u32,
607
608 #[serde(skip_serializing_if = "Option::is_none")]
609 pub result: Option<PeersResult>,
610
611 #[serde(skip_serializing_if = "Option::is_none")]
612 pub error: Option<jsonrpc::ResponseError>,
613}
614
615#[serde_as]
617#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
618#[serde(rename_all = "camelCase")]
619#[derive(Default)]
620pub struct PeersResult {
621 #[serde(rename = "numPeers")]
622 #[serde_as(as = "DisplayFromStr")]
623 pub num_peers: u64,
624 #[serde(skip_serializing_if = "Option::is_none")]
625 pub peers: Option<Vec<Peer>>,
626}
627
628#[serde_as]
631#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
632#[serde(rename_all = "camelCase")]
633pub struct Peer {
634 #[serde_as(as = "crate::codec::serde::ip_port::IpPort")]
635 pub ip: SocketAddr,
636 #[serde(rename = "publicIP")]
637 #[serde_as(as = "crate::codec::serde::ip_port::IpPort")]
638 pub public_ip: SocketAddr,
639 #[serde(rename = "nodeID")]
640 pub node_id: node::Id,
641 pub version: String,
642 #[serde_as(as = "crate::codec::serde::rfc_3339::DateTimeUtc")]
643 pub last_sent: DateTime<Utc>,
644 #[serde_as(as = "crate::codec::serde::rfc_3339::DateTimeUtc")]
645 pub last_received: DateTime<Utc>,
646 #[serde_as(as = "DisplayFromStr")]
647 pub observed_uptime: u32,
648 #[serde_as(as = "HashMap<_, DisplayFromStr>")]
649 pub observed_subnet_uptimes: HashMap<ids::Id, u32>,
650 pub tracked_subnets: Vec<ids::Id>,
651}
652
653impl Default for Peer {
654 fn default() -> Self {
655 Self {
656 ip: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080),
657 public_ip: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080),
658 node_id: node::Id::empty(),
659 version: String::new(),
660 last_sent: DateTime::<Utc>::MIN_UTC,
661 last_received: DateTime::<Utc>::MIN_UTC,
662 observed_uptime: 0,
663 observed_subnet_uptimes: HashMap::new(),
664 tracked_subnets: Vec::new(),
665 }
666 }
667}
668
669#[test]
671fn test_peers() {
672 use std::str::FromStr;
673
674 use chrono::TimeZone;
675
676 let resp: PeersResponse = serde_json::from_str(
678 "
679
680{
681 \"jsonrpc\": \"2.0\",
682 \"result\": {
683 \"numPeers\": \"3\",
684 \"peers\": [
685 {
686 \"ip\": \"206.189.137.87:9651\",
687 \"publicIP\": \"206.189.137.87:9651\",
688 \"nodeID\": \"NodeID-8PYXX47kqLDe2wD4oPbvRRchcnSzMA4J4\",
689 \"version\": \"avalanche/1.9.4\",
690 \"lastSent\": \"2020-06-01T15:23:02Z\",
691 \"lastReceived\": \"2020-06-01T15:22:57Z\",
692 \"benched\": [],
693 \"observedUptime\": \"99\",
694 \"observedSubnetUptimes\": {},
695 \"trackedSubnets\": [],
696 \"benched\": []
697 },
698 {
699 \"ip\": \"158.255.67.151:9651\",
700 \"publicIP\": \"158.255.67.151:9651\",
701 \"nodeID\": \"NodeID-C14fr1n8EYNKyDfYixJ3rxSAVqTY3a8BP\",
702 \"version\": \"avalanche/1.9.4\",
703 \"lastSent\": \"2020-06-01T15:23:02Z\",
704 \"lastReceived\": \"2020-06-01T15:22:34Z\",
705 \"benched\": [],
706 \"observedUptime\": \"75\",
707 \"observedSubnetUptimes\": {
708 \"29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL\": \"100\"
709 },
710 \"trackedSubnets\": [
711 \"29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL\"
712 ],
713 \"benched\": []
714 }
715 ]
716 },
717 \"id\": 1
718}
719
720",
721 )
722 .unwrap();
723
724 let uptimes: HashMap<ids::Id, u32> = [(
725 ids::Id::from_str("29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL").unwrap(),
726 100,
727 )]
728 .iter()
729 .cloned()
730 .collect();
731 let expected = PeersResponse {
732 jsonrpc: "2.0".to_string(),
733 id: 1,
734 result: Some(PeersResult {
735 num_peers: 3,
736 peers: Some(vec![
737 Peer {
738 ip: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(206, 189, 137, 87)), 9651),
739 public_ip: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(206, 189, 137, 87)), 9651),
740 node_id: node::Id::from_str("NodeID-8PYXX47kqLDe2wD4oPbvRRchcnSzMA4J4")
741 .unwrap(),
742 version: String::from("avalanche/1.9.4"),
743 last_sent: Utc.from_utc_datetime(
744 &DateTime::parse_from_rfc3339("2020-06-01T15:23:02Z")
745 .unwrap()
746 .naive_utc(),
747 ),
748 last_received: Utc.from_utc_datetime(
749 &DateTime::parse_from_rfc3339("2020-06-01T15:22:57Z")
750 .unwrap()
751 .naive_utc(),
752 ),
753 observed_uptime: 99,
754 ..Peer::default()
755 },
756 Peer {
757 ip: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(158, 255, 67, 151)), 9651),
758 public_ip: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(158, 255, 67, 151)), 9651),
759 node_id: node::Id::from_str("NodeID-C14fr1n8EYNKyDfYixJ3rxSAVqTY3a8BP")
760 .unwrap(),
761 version: String::from("avalanche/1.9.4"),
762 last_sent: Utc.from_utc_datetime(
763 &DateTime::parse_from_rfc3339("2020-06-01T15:23:02Z")
764 .unwrap()
765 .naive_utc(),
766 ),
767 last_received: Utc.from_utc_datetime(
768 &DateTime::parse_from_rfc3339("2020-06-01T15:22:34Z")
769 .unwrap()
770 .naive_utc(),
771 ),
772 observed_uptime: 75,
773 observed_subnet_uptimes: uptimes,
774 tracked_subnets: vec![ids::Id::from_str(
775 "29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL",
776 )
777 .unwrap()],
778 },
779 ]),
780 }),
781 error: None,
782 };
783 assert_eq!(resp, expected);
784}