1use alloy_genesis::ChainConfig;
4use alloy_primitives::{B256, U256};
5use serde::{Deserialize, Serialize};
6use std::{
7 collections::BTreeMap,
8 net::{IpAddr, SocketAddr},
9};
10
11#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
17pub struct NodeInfo {
18 pub id: String,
20 pub name: String,
22 pub enode: String,
24 pub enr: String,
26 pub ip: IpAddr,
28 pub ports: Ports,
30 #[serde(rename = "listenAddr")]
32 pub listen_addr: SocketAddr,
33 pub protocols: ProtocolInfo,
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
39pub struct Ports {
40 pub discovery: u16,
42 pub listener: u16,
44}
45
46#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
50pub struct ProtocolInfo {
51 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub eth: Option<EthProtocolInfo>,
54 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub snap: Option<SnapProtocolInfo>,
57}
58
59#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
65pub struct EthProtocolInfo {
66 pub network: u64,
68 #[deprecated(
77 since = "0.8.2",
78 note = "`difficulty` is being removed from `admin_nodeInfo`, see https://github.com/ethereum/go-ethereum/pull/30744"
79 )]
80 pub difficulty: Option<U256>,
81 pub genesis: B256,
83 pub config: ChainConfig,
85 pub head: B256,
87}
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
94pub struct SnapProtocolInfo {}
95
96#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
102pub struct PeerProtocolInfo {
103 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub eth: Option<EthPeerInfo>,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
108 pub snap: Option<SnapPeerInfo>,
109 #[serde(flatten, default)]
111 pub other: BTreeMap<String, serde_json::Value>,
112}
113
114#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
117#[serde(untagged)]
118pub enum EthPeerInfo {
119 Info(EthInfo),
121 #[serde(with = "handshake")]
123 Handshake,
124}
125
126#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
132#[serde(default)]
133pub struct EthInfo {
134 pub version: u64,
136}
137
138#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
141#[serde(untagged)]
142pub enum SnapPeerInfo {
143 Info(SnapInfo),
145 #[serde(with = "handshake")]
147 Handshake,
148}
149
150#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
156#[serde(default)]
157pub struct SnapInfo {
158 pub version: u64,
160}
161
162#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
166pub struct PeerInfo {
167 #[serde(default, skip_serializing_if = "Option::is_none")]
169 pub enr: Option<String>,
170 pub enode: String,
172 pub id: String,
174 pub name: String,
176 pub caps: Vec<String>,
178 pub network: PeerNetworkInfo,
180 pub protocols: PeerProtocolInfo,
182}
183
184#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
187#[serde(rename_all = "camelCase")]
188pub struct PeerNetworkInfo {
189 pub local_address: SocketAddr,
191 pub remote_address: SocketAddr,
193 pub inbound: bool,
195 pub trusted: bool,
197 #[serde(rename = "static")]
199 pub static_node: bool,
200}
201
202#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
204#[serde(rename_all = "lowercase")]
205pub enum PeerEventType {
206 Add,
208 Drop,
210 MsgSend,
212 MsgRecv,
214}
215
216#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
221pub struct PeerEvent {
222 #[serde(rename = "type")]
224 pub kind: PeerEventType,
225 pub peer: String,
227 #[serde(default, skip_serializing_if = "Option::is_none")]
229 pub error: Option<String>,
230 #[serde(default, skip_serializing_if = "Option::is_none")]
232 pub protocol: Option<String>,
233 #[serde(default, skip_serializing_if = "Option::is_none")]
235 pub msg_code: Option<u64>,
236 #[serde(default, skip_serializing_if = "Option::is_none")]
238 pub msg_size: Option<u32>,
239 #[serde(default, skip_serializing_if = "Option::is_none")]
241 pub local_address: Option<SocketAddr>,
242 #[serde(default, skip_serializing_if = "Option::is_none")]
244 pub remote_address: Option<SocketAddr>,
245}
246
247mod handshake {
248 use super::*;
249
250 pub(crate) fn deserialize<'de, D: serde::Deserializer<'de>>(
251 deserializer: D,
252 ) -> Result<(), D::Error> {
253 let s = String::deserialize(deserializer)?;
254 if s == "handshake" {
255 Ok(())
256 } else {
257 Err(serde::de::Error::custom(
258 "expected \"handshake\" if protocol info did not appear in the response",
259 ))
260 }
261 }
262
263 pub(crate) fn serialize<S: serde::Serializer>(serializer: S) -> Result<S::Ok, S::Error> {
264 serializer.serialize_str("handshake")
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271 use similar_asserts::assert_eq;
272 use std::str::FromStr;
273
274 #[test]
275 fn deserialize_peer_info() {
276 let response = r#"{
277 "enode":"enode://bb37b7302f79e47c1226d6e3ccf0ef6d51146019efdcc1f6e861fd1c1a78d5e84e486225a6a8a503b93d5c50125ee980835c92bde7f7d12f074c16f4e439a578@127.0.0.1:60872",
278 "id":"ca23c04b7e796da5d6a5f04a62b81c88d41b1341537db85a2b6443e838d8339b",
279 "name":"Geth/v1.10.19-stable/darwin-arm64/go1.18.3",
280 "caps":["eth/66","eth/67","snap/1"],
281 "network":{
282 "localAddress":"127.0.0.1:30304",
283 "remoteAddress":"127.0.0.1:60872",
284 "inbound":true,
285 "trusted":false,
286 "static":false
287 },
288 "protocols":{
289 "eth":{
290 "version":67,
291 "difficulty":0,
292 "head":"0xb04009ddf4b0763f42778e7d5937e49bebf1e11b2d26c9dac6cefb5f84b6f8ea"
293 },
294 "snap":{"version":1}
295 }
296 }"#;
297 let peer_info: PeerInfo = serde_json::from_str(response).unwrap();
298
299 assert_eq!(peer_info.enode, "enode://bb37b7302f79e47c1226d6e3ccf0ef6d51146019efdcc1f6e861fd1c1a78d5e84e486225a6a8a503b93d5c50125ee980835c92bde7f7d12f074c16f4e439a578@127.0.0.1:60872");
300 }
301
302 #[test]
303 fn deserialize_peer_info_handshake() {
304 let response = r#"{
305 "enode": "enode://a997fde0023537ad01e536ebf2eeeb4b4b3d5286707586727b704f32e8e2b4959e08b6db5b27eb6b7e9f6efcbb53657f4e2bd16900aa77a89426dc3382c29ce0@[::1]:60948",
306 "id": "df6f8bc331005962c2ef1f5236486a753bc6b2ddb5ef04370757999d1ca832d4",
307 "name": "Geth/v1.10.26-stable-e5eb32ac/linux-amd64/go1.18.5",
308 "caps": ["eth/66","eth/67","snap/1"],
309 "network":{
310 "localAddress":"[::1]:30304",
311 "remoteAddress":"[::1]:60948",
312 "inbound":true,
313 "trusted":false,
314 "static":false
315 },
316 "protocols":{
317 "eth":"handshake",
318 "snap":"handshake"
319 }
320 }"#;
321
322 let info: PeerInfo = serde_json::from_str(response).unwrap();
323 assert_eq!(info.protocols.eth, Some(EthPeerInfo::Handshake));
324 assert_eq!(info.protocols.snap, Some(SnapPeerInfo::Handshake));
325 }
326
327 #[test]
328 fn deserialize_peer_info_newer() {
329 let response = r#"{
330 "enode": "enode://f769f8cf850dd9f88a13c81ff3e70c3400cf93511c676c6d50f0e359beb43c28388931f64f56ab4110ccced37fb08163b6966fe42b6e15ec647fa8087914463d@127.0.0.1:45591?discport=0",
331 "id": "daa738efebf7e349b9f5b1a91d782e7355060bb15af8570e23463729d0632deb",
332 "name": "Geth/v1.13.14-stable-2bd6bd01/linux-amd64/go1.21.6",
333 "caps": ["eth/68", "snap/1"],
334 "network": {
335 "localAddress": "127.0.0.1:33236",
336 "remoteAddress": "127.0.0.1:45591",
337 "inbound": false,
338 "trusted": false,
339 "static": true
340 },
341 "protocols": { "eth": { "version": 68 }, "snap": { "version": 1 } }
342 }"#;
343
344 let info: PeerInfo = serde_json::from_str(response).unwrap();
345 assert_eq!(info.protocols.eth, Some(EthPeerInfo::Info(EthInfo { version: 68 })));
346 assert_eq!(info.protocols.snap, Some(SnapPeerInfo::Info(SnapInfo { version: 1 })));
347 }
348
349 #[test]
350 fn deserialize_node_info() {
351 let response = r#"{
353 "id":"6e2fe698f3064cd99410926ce16734e35e3cc947d4354461d2594f2d2dd9f7b6",
354 "name":"Geth/v1.10.19-stable/darwin-arm64/go1.18.3",
355 "enode":"enode://d7dfaea49c7ef37701e668652bcf1bc63d3abb2ae97593374a949e175e4ff128730a2f35199f3462a56298b981dfc395a5abebd2d6f0284ffe5bdc3d8e258b86@127.0.0.1:30304?discport=0",
356 "enr":"enr:-Jy4QIvS0dKBLjTTV_RojS8hjriwWsJNHRVyOh4Pk4aUXc5SZjKRVIOeYc7BqzEmbCjLdIY4Ln7x5ZPf-2SsBAc2_zqGAYSwY1zog2V0aMfGhNegsXuAgmlkgnY0gmlwhBiT_DiJc2VjcDI1NmsxoQLX366knH7zdwHmaGUrzxvGPTq7Kul1kzdKlJ4XXk_xKIRzbmFwwIN0Y3CCdmA",
357 "ip":"127.0.0.1",
358 "ports":{
359 "discovery":0,
360 "listener":30304
361 },
362 "listenAddr":"[::]:30304",
363 "protocols":{
364 "eth":{
365 "network":1337,
366 "difficulty":0,
367 "genesis":"0xb04009ddf4b0763f42778e7d5937e49bebf1e11b2d26c9dac6cefb5f84b6f8ea",
368 "config":{
369 "chainId":0,
370 "eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000"
371 },
372 "head":"0xb04009ddf4b0763f42778e7d5937e49bebf1e11b2d26c9dac6cefb5f84b6f8ea"
373 },
374 "snap":{}
375 }
376 }"#;
377
378 let _: NodeInfo = serde_json::from_str(response).unwrap();
379 }
380
381 #[test]
382 fn deserialize_node_info_post_merge() {
383 let response = r#"{
385 "id":"6e2fe698f3064cd99410926ce16734e35e3cc947d4354461d2594f2d2dd9f7b6",
386 "name":"Geth/v1.10.19-stable/darwin-arm64/go1.18.3",
387 "enode":"enode://d7dfaea49c7ef37701e668652bcf1bc63d3abb2ae97593374a949e175e4ff128730a2f35199f3462a56298b981dfc395a5abebd2d6f0284ffe5bdc3d8e258b86@127.0.0.1:30304?discport=0",
388 "enr":"enr:-Jy4QIvS0dKBLjTTV_RojS8hjriwWsJNHRVyOh4Pk4aUXc5SZjKRVIOeYc7BqzEmbCjLdIY4Ln7x5ZPf-2SsBAc2_zqGAYSwY1zog2V0aMfGhNegsXuAgmlkgnY0gmlwhBiT_DiJc2VjcDI1NmsxoQLX366knH7zdwHmaGUrzxvGPTq7Kul1kzdKlJ4XXk_xKIRzbmFwwIN0Y3CCdmA",
389 "ip":"127.0.0.1",
390 "ports":{
391 "discovery":0,
392 "listener":30304
393 },
394 "listenAddr":"[::]:30304",
395 "protocols":{
396 "eth":{
397 "network":1337,
398 "difficulty":0,
399 "genesis":"0xb04009ddf4b0763f42778e7d5937e49bebf1e11b2d26c9dac6cefb5f84b6f8ea",
400 "config":{
401 "chainId":0,
402 "eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000",
403 "terminalTotalDifficulty": "0xC70D808A128D7380000",
404 "terminalTotalDifficultyPassed":true,
405 "ethash":{}
406 },
407 "head":"0xb04009ddf4b0763f42778e7d5937e49bebf1e11b2d26c9dac6cefb5f84b6f8ea"
408 },
409 "snap":{}
410 }
411 }"#;
412
413 let _: NodeInfo = serde_json::from_str(response).unwrap();
414 }
415
416 #[test]
417 fn deserialize_node_info_mainnet_full() {
418 let actual_response = r#"{
419 "id": "74477ca052fcf55ee9eafb369fafdb3e91ad7b64fbd7ae15a4985bfdc43696f2",
420 "name": "Geth/v1.10.26-stable/darwin-arm64/go1.19.3",
421 "enode": "enode://962184c6f2a19e064e2ddf0d5c5a788c8c5ed3a4909b7f75fb4dad967392ff542772bcc498cd7f15e13eecbde830265f379779c6da1f71fb8fe1a4734dfc0a1e@127.0.0.1:13337?discport=0",
422 "enr": "enr:-J-4QFttJyL3f2-B2TQmBZNFxex99TSBv1YtB_8jqUbXWkf6LOREKQAPW2bIn8kJ8QvHbWxCQNFzTX6sehjbrz1ZkSuGAYSyQ0_rg2V0aMrJhPxk7ASDEYwwgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQKWIYTG8qGeBk4t3w1cWniMjF7TpJCbf3X7Ta2Wc5L_VIRzbmFwwIN0Y3CCNBk",
423 "ip": "127.0.0.1",
424 "ports": {
425 "discovery": 0,
426 "listener": 13337
427 },
428 "listenAddr": "[::]:13337",
429 "protocols": {
430 "eth": {
431 "network": 1337,
432 "difficulty": 17179869184,
433 "genesis": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3",
434 "config": {
435 "chainId": 1,
436 "homesteadBlock": 1150000,
437 "daoForkBlock": 1920000,
438 "daoForkSupport": true,
439 "eip150Block": 2463000,
440 "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0",
441 "eip155Block": 2675000,
442 "eip158Block": 2675000,
443 "byzantiumBlock": 4370000,
444 "constantinopleBlock": 7280000,
445 "petersburgBlock": 7280000,
446 "istanbulBlock": 9069000,
447 "muirGlacierBlock": 9200000,
448 "berlinBlock": 12244000,
449 "londonBlock": 12965000,
450 "arrowGlacierBlock": 13773000,
451 "grayGlacierBlock": 15050000,
452 "terminalTotalDifficulty": "0xC70D808A128D7380000",
453 "terminalTotalDifficultyPassed": true,
454 "ethash": {}
455 },
456 "head": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
457 },
458 "snap": {}
459 }
460 }"#;
461
462 let _: NodeInfo = serde_json::from_str(actual_response).unwrap();
463 }
464
465 #[test]
466 fn serialize_deserialize_node_info_roundtrip() {
467 let node_info = NodeInfo {
468 id: "6e2fe698f3064cd99410926ce16734e35e3cc947d4354461d2594f2d2dd9f7b6".to_string(),
469 name: "Geth/v1.10.19-stable/darwin-arm64/go1.18.3".to_string(),
470 enode: "enode://d7dfaea49c7ef37701e668652bcf1bc63d3abb2ae97593374a949e175e4ff128730a2f35199f3462a56298b981dfc395a5abebd2d6f0284ffe5bdc3d8e258b86@127.0.0.1:30304?discport=0".to_string(),
471 enr: "enr:-Jy4QIvS0dKBLjTTV_RojS8hjriwWsJNHRVyOh4Pk4aUXc5SZjKRVIOeYc7BqzEmbCjLdIY4Ln7x5ZPf-2SsBAc2_zqGAYSwY1zog2V0aMfGhNegsXuAgmlkgnY0gmlwhBiT_DiJc2VjcDI1NmsxoQLX366knH7zdwHmaGUrzxvGPTq7Kul1kzdKlJ4XXk_xKIRzbmFwwIN0Y3CCdmA".to_string(),
472 ip: "127.0.0.1".parse().unwrap(),
473 ports: Ports {
474 discovery: 0,
475 listener: 30304,
476 },
477 listen_addr: "[::]:30304".parse().unwrap(),
478 protocols: ProtocolInfo {
479 eth: Some(EthProtocolInfo {
480 network: 1337,
481 #[allow(deprecated)]
482 difficulty: Some(U256::from(0)),
483 genesis: B256::from_str("0xb04009ddf4b0763f42778e7d5937e49bebf1e11b2d26c9dac6cefb5f84b6f8ea").unwrap(),
484 config: ChainConfig::default(),
485 head: B256::from_str("0xb04009ddf4b0763f42778e7d5937e49bebf1e11b2d26c9dac6cefb5f84b6f8ea").unwrap(),
486 }),
487 snap: Some(SnapProtocolInfo {}),
488 },
489 };
490
491 let serialized = serde_json::to_string(&node_info).expect("Serialization failed");
492 let deserialized: NodeInfo =
493 serde_json::from_str(&serialized).expect("Deserialization failed");
494
495 assert_eq!(node_info, deserialized);
496 }
497
498 #[test]
499 fn serialize_deserialize_peer_info_roundtrip() {
500 let peer_info = PeerInfo {
501 enr: Some("enr:-Jy4QIvS0dKBLjTTV_RojS8hjriwWsJNHRVyOh4Pk4aUXc5SZjKRVIOeYc7BqzEmbCjLdIY4Ln7x5ZPf-2SsBAc2_zqGAYSwY1zog2V0aMfGhNegsXuAgmlkgnY0gmlwhBiT_DiJc2VjcDI1NmsxoQLX366knH7zdwHmaGUrzxvGPTq7Kul1kzdKlJ4XXk_xKIRzbmFwwIN0Y3CCdmA".to_string()),
502 enode: "enode://bb37b7302f79e47c1226d6e3ccf0ef6d51146019efdcc1f6e861fd1c1a78d5e84e486225a6a8a503b93d5c50125ee980835c92bde7f7d12f074c16f4e439a578@127.0.0.1:60872".to_string(),
503 id: "ca23c04b7e796da5d6a5f04a62b81c88d41b1341537db85a2b6443e838d8339b".to_string(),
504 name: "Geth/v1.10.19-stable/darwin-arm64/go1.18.3".to_string(),
505 caps: vec!["eth/66".to_string(), "eth/67".to_string(), "snap/1".to_string()],
506 network: PeerNetworkInfo {
507 local_address: "127.0.0.1:30304".parse().unwrap(),
508 remote_address: "127.0.0.1:60872".parse().unwrap(),
509 inbound: true,
510 trusted: false,
511 static_node: false,
512 },
513 protocols: PeerProtocolInfo {
514 eth: Some(EthPeerInfo::Info(EthInfo { version: 67 })),
515 snap: Some(SnapPeerInfo::Info(SnapInfo { version: 1 })),
516 other: BTreeMap::new(),
517 },
518 };
519
520 let serialized = serde_json::to_string(&peer_info).expect("Serialization failed");
521 let deserialized: PeerInfo =
522 serde_json::from_str(&serialized).expect("Deserialization failed");
523
524 assert_eq!(peer_info, deserialized);
525 }
526
527 #[test]
528 fn serialize_deserialize_peer_info_handshake_roundtrip() {
529 let peer_info = PeerInfo {
530 enr: None,
531 enode: "enode://a997fde0023537ad01e536ebf2eeeb4b4b3d5286707586727b704f32e8e2b4959e08b6db5b27eb6b7e9f6efcbb53657f4e2bd16900aa77a89426dc3382c29ce0@[::1]:60948".to_string(),
532 id: "df6f8bc331005962c2ef1f5236486a753bc6b2ddb5ef04370757999d1ca832d4".to_string(),
533 name: "Geth/v1.10.26-stable-e5eb32ac/linux-amd64/go1.18.5".to_string(),
534 caps: vec!["eth/66".to_string(), "eth/67".to_string(), "snap/1".to_string()],
535 network: PeerNetworkInfo {
536 local_address: "[::1]:30304".parse().unwrap(),
537 remote_address: "[::1]:60948".parse().unwrap(),
538 inbound: true,
539 trusted: false,
540 static_node: false,
541 },
542 protocols: PeerProtocolInfo {
543 eth: Some(EthPeerInfo::Handshake),
544 snap: Some(SnapPeerInfo::Handshake),
545 other: BTreeMap::new(),
546 },
547 };
548
549 let serialized = serde_json::to_string(&peer_info).expect("Serialization failed");
550 let deserialized: PeerInfo =
551 serde_json::from_str(&serialized).expect("Deserialization failed");
552
553 assert_eq!(peer_info, deserialized);
554 }
555
556 #[test]
557 fn serialize_deserialize_peer_info_with_optional_fields_roundtrip() {
558 let peer_info = PeerInfo {
559 enr: None,
560 enode: "enode://f769f8cf850dd9f88a13c81ff3e70c3400cf93511c676c6d50f0e359beb43c28388931f64f56ab4110ccced37fb08163b6966fe42b6e15ec647fa8087914463d@127.0.0.1:45591?discport=0".to_string(),
561 id: "daa738efebf7e349b9f5b1a91d782e7355060bb15af8570e23463729d0632deb".to_string(),
562 name: "Geth/v1.13.14-stable-2bd6bd01/linux-amd64/go1.21.6".to_string(),
563 caps: vec!["eth/68".to_string(), "snap/1".to_string()],
564 network: PeerNetworkInfo {
565 local_address: "127.0.0.1:33236".parse().unwrap(),
566 remote_address: "127.0.0.1:45591".parse().unwrap(),
567 inbound: false,
568 trusted: false,
569 static_node: true,
570 },
571 protocols: PeerProtocolInfo {
572 eth: Some(EthPeerInfo::Info(EthInfo { version: 68 })),
573 snap: Some(SnapPeerInfo::Info(SnapInfo { version: 1 })),
574 other: BTreeMap::new(),
575 },
576 };
577
578 let serialized = serde_json::to_string(&peer_info).expect("Serialization failed");
579 let deserialized: PeerInfo =
580 serde_json::from_str(&serialized).expect("Deserialization failed");
581
582 assert_eq!(peer_info, deserialized);
583 }
584
585 #[test]
586 fn serialize_deserialize_peer_event_roundtrip() {
587 let peer_event = PeerEvent {
588 kind: PeerEventType::Add,
589 peer: "ca23c04b7e796da5d6a5f04a62b81c88d41b1341537db85a2b6443e838d8339b".to_string(),
590 error: None,
591 protocol: None,
592 msg_code: None,
593 msg_size: None,
594 local_address: Some("127.0.0.1:30304".parse().unwrap()),
595 remote_address: Some("127.0.0.1:60872".parse().unwrap()),
596 };
597
598 let serialized = serde_json::to_string(&peer_event).expect("Serialization failed");
599 let deserialized: PeerEvent =
600 serde_json::from_str(&serialized).expect("Deserialization failed");
601
602 assert_eq!(peer_event, deserialized);
603 }
604
605 #[test]
606 fn serialize_deserialize_peer_event_with_all_fields_roundtrip() {
607 let peer_event = PeerEvent {
608 kind: PeerEventType::MsgRecv,
609 peer: "df6f8bc331005962c2ef1f5236486a753bc6b2ddb5ef04370757999d1ca832d4".to_string(),
610 error: Some("connection error".to_string()),
611 protocol: Some("eth".to_string()),
612 msg_code: Some(0x10),
613 msg_size: Some(1024),
614 local_address: Some("[::1]:30304".parse().unwrap()),
615 remote_address: Some("[::1]:60948".parse().unwrap()),
616 };
617
618 let serialized = serde_json::to_string(&peer_event).expect("Serialization failed");
619 let deserialized: PeerEvent =
620 serde_json::from_str(&serialized).expect("Deserialization failed");
621
622 assert_eq!(peer_event, deserialized);
623 }
624
625 #[test]
626 fn serialize_deserialize_peer_event_all_types_roundtrip() {
627 let event_types = vec![
628 PeerEventType::Add,
629 PeerEventType::Drop,
630 PeerEventType::MsgSend,
631 PeerEventType::MsgRecv,
632 ];
633
634 for event_type in event_types {
635 let peer_event = PeerEvent {
636 kind: event_type,
637 peer: "test_peer_id".to_string(),
638 error: None,
639 protocol: None,
640 msg_code: None,
641 msg_size: None,
642 local_address: None,
643 remote_address: None,
644 };
645
646 let serialized = serde_json::to_string(&peer_event).expect("Serialization failed");
647 let deserialized: PeerEvent =
648 serde_json::from_str(&serialized).expect("Deserialization failed");
649
650 assert_eq!(peer_event, deserialized);
651 }
652 }
653}