Skip to main content

alloy_rpc_types_beacon/
node.rs

1//! Types for <https://ethereum.github.io/beacon-APIs/#/Node>
2#![allow(missing_docs)]
3
4use serde::{Deserialize, Serialize};
5use serde_with::{serde_as, DisplayFromStr};
6
7/// Response from the [`/eth/v1/node/syncing`](https://ethereum.github.io/beacon-APIs/#/Node/getSyncingStatus) endpoint.
8#[serde_as]
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SyncStatus {
11    #[serde_as(as = "DisplayFromStr")]
12    pub head_slot: u64,
13    #[serde_as(as = "DisplayFromStr")]
14    pub sync_distance: usize,
15    pub is_syncing: bool,
16    #[serde(default)]
17    pub is_optimistic: bool,
18    #[serde(default)]
19    pub el_offline: bool,
20}
21
22/// Response from the [`/eth/v1/node/health`](https://ethereum.github.io/beacon-APIs/#/Node/getHealth) endpoint.
23#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
24pub enum HealthStatus {
25    Ready,
26    Syncing,
27    NotInitialized,
28    Unknown,
29}
30
31/// Response from the [`/eth/v1/node/version`](https://ethereum.github.io/beacon-APIs/#/Node/getNodeVersion) endpoint.
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct VersionData {
34    pub version: String,
35}
36
37/// Response from the [`/eth/v1/node/identity`](https://ethereum.github.io/beacon-APIs/#/Node/getNetworkIdentity) endpoint.
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct NodeIdentity {
40    pub peer_id: String,
41    pub enr: String,
42    pub p2p_addresses: Vec<String>,
43    pub discovery_addresses: Vec<String>,
44    pub metadata: IdentityMetadata,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct IdentityMetadata {
49    pub seq_number: String,
50    pub attnets: String,
51    pub syncnets: String,
52}
53
54/// Response from the `eth/v1/node/peers` endpoint
55#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
56#[serde(rename_all = "lowercase")]
57pub enum PeerDirection {
58    Inbound,
59    Outbound,
60}
61
62/// Response from the `eth/v1/node/peers` endpoint
63#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
64#[serde(rename_all = "lowercase")]
65pub enum PeerState {
66    Disconnected,
67    Connecting,
68    Connected,
69    Disconnecting,
70}
71
72/// Metadata about an individual peer.
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct PeerInfo {
75    pub peer_id: String,
76    pub enr: Option<String>,
77    pub last_seen_p2p_address: String,
78    pub state: PeerState,
79    pub direction: PeerDirection,
80}
81
82/// Metadata returned with peer list.
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct PeersMeta {
85    pub count: usize,
86}
87
88/// Response from the [`/eth/v1/node/peers`](https://ethereum.github.io/beacon-APIs/#/Node/getPeers) endpoint.
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct PeersResponse {
91    pub data: Vec<PeerInfo>,
92    pub meta: PeersMeta,
93}
94
95/// Response from the [`/eth/v1/node/peer_count`](https://ethereum.github.io/beacon-APIs/#/Node/getPeerCount) endpoint.
96#[serde_as]
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct PeerCount {
99    #[serde_as(as = "DisplayFromStr")]
100    pub disconnected: usize,
101    #[serde_as(as = "DisplayFromStr")]
102    pub connecting: usize,
103    #[serde_as(as = "DisplayFromStr")]
104    pub connected: usize,
105    #[serde_as(as = "DisplayFromStr")]
106    pub disconnecting: usize,
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn test_sync_status() {
115        let s = r#"{
116    "head_slot": "1",
117    "sync_distance": "1",
118    "is_syncing": true,
119    "is_optimistic": true,
120    "el_offline": true
121  }"#;
122
123        let _sync_status: SyncStatus = serde_json::from_str(s).unwrap();
124    }
125
126    #[test]
127    fn test_identity_with_null_enr() {
128        let s = r#"
129        {
130            "peer_id": "QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N",
131            "enr": "enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8",
132            "p2p_addresses": [
133              "/ip4/7.7.7.7/tcp/4242/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N"
134            ],
135            "discovery_addresses": [
136              "/ip4/7.7.7.7/udp/30303/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N"
137            ],
138            "metadata": {
139              "seq_number": "1",
140              "attnets": "0x0000000000000000",
141              "syncnets": "0x0f"
142            }
143        }
144        "#;
145
146        let _result: NodeIdentity = serde_json::from_str(s).unwrap();
147    }
148
149    #[test]
150    fn test_peer_count_response() {
151        let json = r#"
152        {
153            "disconnected": "1",
154            "connecting": "1",
155            "connected": "1",
156            "disconnecting": "1"
157        }
158        "#;
159
160        let _parsed: PeerCount = serde_json::from_str(json).unwrap();
161    }
162
163    #[test]
164    fn test_peers_response() {
165        let json = r#"
166        {
167            "data": [
168                {
169                    "peer_id": "QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N",
170                    "enr": null,
171                    "last_seen_p2p_address": "/ip4/7.7.7.7/tcp/4242/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N",
172                    "state": "disconnected",
173                    "direction": "inbound"
174                }
175            ],
176            "meta": {
177                "count": 1
178            }
179        }
180        "#;
181
182        let _parsed: PeersResponse = serde_json::from_str(json).unwrap();
183    }
184}