use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec};
use alloy_primitives::{B512, U256};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct SyncInfo {
pub starting_block: U256,
pub current_block: U256,
pub highest_block: U256,
pub warp_chunks_amount: Option<U256>,
pub warp_chunks_processed: Option<U256>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub stages: Option<Vec<Stage>>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct Stage {
#[cfg_attr(feature = "serde", serde(alias = "stage_name"))]
pub name: String,
#[cfg_attr(feature = "serde", serde(alias = "block_number", with = "alloy_serde::quantity"))]
pub block: u64,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Peers {
pub active: usize,
pub connected: usize,
pub max: u32,
pub peers: Vec<PeerInfo>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PeerInfo {
pub id: Option<String>,
pub name: String,
pub caps: Vec<String>,
pub network: PeerNetworkInfo,
pub protocols: PeerProtocolsInfo,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct PeerNetworkInfo {
pub remote_address: String,
pub local_address: String,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PeerProtocolsInfo {
pub eth: Option<PeerEthProtocolInfo>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub pip: Option<PipProtocolInfo>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PeerEthProtocolInfo {
pub version: u32,
pub difficulty: Option<U256>,
pub head: String,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PipProtocolInfo {
pub version: u32,
pub difficulty: U256,
pub head: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SyncStatus {
Info(Box<SyncInfo>),
None,
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for SyncStatus {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
#[serde(untagged)]
enum Syncing {
None(bool),
IsSyncing(Box<SyncInfo>),
}
match Syncing::deserialize(deserializer)? {
Syncing::None(false) => Ok(Self::None),
Syncing::None(true) => Err(serde::de::Error::custom(
"eth_syncing returned `true` that is undefined value.",
)),
Syncing::IsSyncing(sync) => Ok(Self::Info(sync)),
}
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for SyncStatus {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Self::Info(info) => info.serialize(serializer),
Self::None => serializer.serialize_bool(false),
}
}
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[doc(alias = "TxStats")]
pub struct TransactionStats {
pub first_seen: u64,
pub propagated_to: BTreeMap<B512, usize>,
}
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct ChainStatus {
pub block_gap: Option<(U256, U256)>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sync_info_serialization() {
let sync_info = SyncInfo {
starting_block: U256::from(0x3cbed5),
current_block: U256::from(0x3cf522),
highest_block: U256::from(0x3e0e41),
warp_chunks_amount: Some(U256::from(10)),
warp_chunks_processed: Some(U256::from(5)),
stages: Some(vec![
Stage { name: "Stage 1".to_string(), block: 1000 },
Stage { name: "Stage 2".to_string(), block: 2000 },
]),
};
let serialized = serde_json::to_string(&sync_info).expect("Serialization failed");
let deserialized: SyncInfo =
serde_json::from_str(&serialized).expect("Deserialization failed");
assert_eq!(sync_info, deserialized);
}
#[test]
fn test_peer_info_serialization() {
let peer_info = PeerInfo {
id: Some("peer_id_123".to_string()),
name: "GethClient".to_string(),
caps: vec!["eth/66".to_string(), "les/2".to_string()],
network: PeerNetworkInfo {
remote_address: "192.168.1.1:30303".to_string(),
local_address: "127.0.0.1:30303".to_string(),
},
protocols: PeerProtocolsInfo {
eth: Some(PeerEthProtocolInfo {
version: 66,
difficulty: Some(U256::from(1000000)),
head: "0xabcdef".to_string(),
}),
pip: None,
},
};
let serialized = serde_json::to_string(&peer_info).expect("Serialization failed");
let deserialized: PeerInfo =
serde_json::from_str(&serialized).expect("Deserialization failed");
assert_eq!(peer_info, deserialized);
}
#[test]
fn test_sync_status_serialization() {
let sync_status = SyncStatus::Info(Box::new(SyncInfo {
starting_block: U256::from(0x3cbed5),
current_block: U256::from(0x3cf522),
highest_block: U256::from(0x3e0e41),
warp_chunks_amount: None,
warp_chunks_processed: None,
stages: None,
}));
let serialized = serde_json::to_string(&sync_status).expect("Serialization failed");
let deserialized: SyncStatus =
serde_json::from_str(&serialized).expect("Deserialization failed");
assert_eq!(sync_status, deserialized);
let none_status = SyncStatus::None;
let serialized_none = serde_json::to_string(&none_status).expect("Serialization failed");
let deserialized_none: SyncStatus =
serde_json::from_str(&serialized_none).expect("Deserialization failed");
assert_eq!(none_status, deserialized_none);
}
}