alloy_rpc_types_eth/
syncing.rs

1use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec};
2use alloy_primitives::{B512, U256};
3
4/// Syncing info
5#[derive(Clone, Debug, Default, PartialEq, Eq)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
8pub struct SyncInfo {
9    /// Starting block
10    pub starting_block: U256,
11    /// Current block
12    pub current_block: U256,
13    /// Highest block seen so far
14    pub highest_block: U256,
15    /// Warp sync snapshot chunks total.
16    pub warp_chunks_amount: Option<U256>,
17    /// Warp sync snapshot chunks processed.
18    pub warp_chunks_processed: Option<U256>,
19    /// The details of the sync stages as an hashmap
20    /// where the key is the name of the stage and the value is the block number.
21    #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
22    pub stages: Option<Vec<Stage>>,
23}
24
25/// The detail of the sync stages.
26#[derive(Clone, Debug, Default, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
29pub struct Stage {
30    /// The name of the sync stage.
31    #[cfg_attr(feature = "serde", serde(alias = "stage_name"))]
32    pub name: String,
33    /// Indicates the progress of the sync stage.
34    #[cfg_attr(feature = "serde", serde(alias = "block_number", with = "alloy_serde::quantity"))]
35    pub block: u64,
36}
37
38/// Peers info
39#[derive(Clone, Debug, Default, PartialEq, Eq)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41pub struct Peers {
42    /// Number of active peers
43    pub active: usize,
44    /// Number of connected peers
45    pub connected: usize,
46    /// Max number of peers
47    pub max: u32,
48    /// Detailed information on peers
49    pub peers: Vec<PeerInfo>,
50}
51
52/// Peer connection information
53#[derive(Clone, Debug, Default, PartialEq, Eq)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
55pub struct PeerInfo {
56    /// Public node id
57    pub id: Option<String>,
58    /// Node client ID
59    pub name: String,
60    /// Capabilities
61    pub caps: Vec<String>,
62    /// Network information
63    pub network: PeerNetworkInfo,
64    /// Protocols information
65    pub protocols: PeerProtocolsInfo,
66}
67
68/// Peer network information
69#[derive(Clone, Debug, Default, PartialEq, Eq)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
72pub struct PeerNetworkInfo {
73    /// Remote endpoint address
74    pub remote_address: String,
75    /// Local endpoint address
76    pub local_address: String,
77}
78
79/// Peer protocols information
80#[derive(Clone, Debug, Default, PartialEq, Eq)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82pub struct PeerProtocolsInfo {
83    /// Ethereum protocol information
84    pub eth: Option<PeerEthProtocolInfo>,
85    /// PIP protocol information.
86    #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
87    pub pip: Option<PipProtocolInfo>,
88}
89
90/// Peer Ethereum protocol information
91#[derive(Clone, Debug, Default, PartialEq, Eq)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
93pub struct PeerEthProtocolInfo {
94    /// Negotiated ethereum protocol version
95    pub version: u32,
96    /// Peer total difficulty if known
97    pub difficulty: Option<U256>,
98    /// SHA3 of peer best block hash
99    pub head: String,
100}
101
102/// Peer PIP protocol information
103#[derive(Clone, Debug, Default, PartialEq, Eq)]
104#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
105pub struct PipProtocolInfo {
106    /// Negotiated PIP protocol version
107    pub version: u32,
108    /// Peer total difficulty
109    pub difficulty: U256,
110    /// SHA3 of peer best block hash
111    pub head: String,
112}
113
114/// Sync status
115#[derive(Clone, Debug, PartialEq, Eq)]
116pub enum SyncStatus {
117    /// Info when syncing
118    Info(Box<SyncInfo>),
119    /// Not syncing
120    None,
121}
122
123#[cfg(feature = "serde")]
124impl<'de> serde::Deserialize<'de> for SyncStatus {
125    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
126    where
127        D: serde::Deserializer<'de>,
128    {
129        #[derive(serde::Deserialize)]
130        #[serde(untagged)]
131        enum Syncing {
132            /// When client is synced to the highest block, eth_syncing with return "false"
133            None(bool),
134            IsSyncing(Box<SyncInfo>),
135        }
136
137        match Syncing::deserialize(deserializer)? {
138            Syncing::None(false) => Ok(Self::None),
139            Syncing::None(true) => Err(serde::de::Error::custom(
140                "eth_syncing returned `true` that is undefined value.",
141            )),
142            Syncing::IsSyncing(sync) => Ok(Self::Info(sync)),
143        }
144    }
145}
146
147#[cfg(feature = "serde")]
148impl serde::Serialize for SyncStatus {
149    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
150    where
151        S: serde::Serializer,
152    {
153        match self {
154            Self::Info(info) => info.serialize(serializer),
155            Self::None => serializer.serialize_bool(false),
156        }
157    }
158}
159
160/// Propagation statistics for pending transaction.
161#[derive(Clone, Debug, Default)]
162#[cfg_attr(feature = "serde", derive(serde::Serialize))]
163#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
164#[doc(alias = "TxStats")]
165pub struct TransactionStats {
166    /// Block no this transaction was first seen.
167    pub first_seen: u64,
168    /// Peers this transaction was propagated to with count.
169    pub propagated_to: BTreeMap<B512, usize>,
170}
171
172/// Chain status.
173#[derive(Clone, Copy, Debug, Default)]
174#[cfg_attr(feature = "serde", derive(serde::Serialize))]
175#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
176pub struct ChainStatus {
177    /// Describes the gap in the blockchain, if there is one: (first, last)
178    pub block_gap: Option<(U256, U256)>,
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184
185    #[test]
186    #[cfg(feature = "serde")]
187    fn test_sync_info_serialization() {
188        let sync_info = SyncInfo {
189            starting_block: U256::from(0x3cbed5),
190            current_block: U256::from(0x3cf522),
191            highest_block: U256::from(0x3e0e41),
192            warp_chunks_amount: Some(U256::from(10)),
193            warp_chunks_processed: Some(U256::from(5)),
194            stages: Some(vec![
195                Stage { name: "Stage 1".to_string(), block: 1000 },
196                Stage { name: "Stage 2".to_string(), block: 2000 },
197            ]),
198        };
199
200        let serialized = serde_json::to_string(&sync_info).expect("Serialization failed");
201        let deserialized: SyncInfo =
202            serde_json::from_str(&serialized).expect("Deserialization failed");
203
204        assert_eq!(sync_info, deserialized);
205    }
206
207    #[test]
208    #[cfg(feature = "serde")]
209    fn test_peer_info_serialization() {
210        let peer_info = PeerInfo {
211            id: Some("peer_id_123".to_string()),
212            name: "GethClient".to_string(),
213            caps: vec!["eth/66".to_string(), "les/2".to_string()],
214            network: PeerNetworkInfo {
215                remote_address: "192.168.1.1:30303".to_string(),
216                local_address: "127.0.0.1:30303".to_string(),
217            },
218            protocols: PeerProtocolsInfo {
219                eth: Some(PeerEthProtocolInfo {
220                    version: 66,
221                    difficulty: Some(U256::from(1000000)),
222                    head: "0xabcdef".to_string(),
223                }),
224                pip: None,
225            },
226        };
227
228        let serialized = serde_json::to_string(&peer_info).expect("Serialization failed");
229        let deserialized: PeerInfo =
230            serde_json::from_str(&serialized).expect("Deserialization failed");
231
232        assert_eq!(peer_info, deserialized);
233    }
234
235    #[test]
236    #[cfg(feature = "serde")]
237    fn test_sync_status_serialization() {
238        let sync_status = SyncStatus::Info(Box::new(SyncInfo {
239            starting_block: U256::from(0x3cbed5),
240            current_block: U256::from(0x3cf522),
241            highest_block: U256::from(0x3e0e41),
242            warp_chunks_amount: None,
243            warp_chunks_processed: None,
244            stages: None,
245        }));
246
247        let serialized = serde_json::to_string(&sync_status).expect("Serialization failed");
248        let deserialized: SyncStatus =
249            serde_json::from_str(&serialized).expect("Deserialization failed");
250
251        assert_eq!(sync_status, deserialized);
252
253        let none_status = SyncStatus::None;
254        let serialized_none = serde_json::to_string(&none_status).expect("Serialization failed");
255        let deserialized_none: SyncStatus =
256            serde_json::from_str(&serialized_none).expect("Deserialization failed");
257
258        assert_eq!(none_status, deserialized_none);
259    }
260}