From b3de7adae7138d292ae303451a06a431028ff020 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 6 Feb 2023 22:12:02 +0100 Subject: [PATCH] fix(rpc): make NodeInfo behave as geth nodeInfo (#1200) --- crates/net/network-api/src/lib.rs | 6 ++-- crates/primitives/src/jsonu256.rs | 15 ++++++++++ crates/primitives/src/lib.rs | 9 ++++-- crates/primitives/src/net.rs | 12 +++++++- crates/rpc/rpc-types/src/admin.rs | 46 +++++++++++++++++++++++++------ 5 files changed, 74 insertions(+), 14 deletions(-) diff --git a/crates/net/network-api/src/lib.rs b/crates/net/network-api/src/lib.rs index 73197b7a0..708544c9d 100644 --- a/crates/net/network-api/src/lib.rs +++ b/crates/net/network-api/src/lib.rs @@ -86,7 +86,7 @@ pub enum PeerKind { } /// The status of the network being ran by the local node. -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct NetworkStatus { /// The local node client version. pub client_version: String, @@ -95,11 +95,11 @@ pub struct NetworkStatus { /// Information about the Ethereum Wire Protocol. pub eth_protocol_info: EthProtocolInfo, } - /// Information about the Ethereum Wire Protocol (ETH) -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct EthProtocolInfo { /// The current difficulty at the head of the chain. + #[serde(deserialize_with = "reth_primitives::serde_helper::deserialize_json_u256")] pub difficulty: U256, /// The block hash of the head of the chain. pub head: H256, diff --git a/crates/primitives/src/jsonu256.rs b/crates/primitives/src/jsonu256.rs index ee39e1d1b..df7e13895 100644 --- a/crates/primitives/src/jsonu256.rs +++ b/crates/primitives/src/jsonu256.rs @@ -9,6 +9,12 @@ use std::{fmt, str::FromStr}; #[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] pub struct JsonU256(pub U256); +impl From for U256 { + fn from(value: JsonU256) -> Self { + value.0 + } +} + impl Serialize for JsonU256 { fn serialize(&self, serializer: S) -> Result where @@ -69,6 +75,15 @@ impl<'a> Visitor<'a> for JsonU256Visitor { } } +/// Supports parsing `U256` numbers as strings via [JsonU256] +pub fn deserialize_json_u256<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let num = JsonU256::deserialize(deserializer)?; + Ok(num.into()) +} + #[cfg(test)] mod test { use super::JsonU256; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 609eda717..010cdff5b 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -96,13 +96,18 @@ mod __reexport { pub use tiny_keccak; } +// Useful reexports +pub use __reexport::*; + /// Various utilities pub mod utils { pub use ethers_core::types::serde_helpers; } -// Useful reexports -pub use __reexport::*; +/// Helpers for working with serde +pub mod serde_helper { + pub use crate::jsonu256::deserialize_json_u256; +} /// Returns the keccak256 hash for the given data. #[inline] diff --git a/crates/primitives/src/net.rs b/crates/primitives/src/net.rs index c40490135..bfb990f9d 100644 --- a/crates/primitives/src/net.rs +++ b/crates/primitives/src/net.rs @@ -93,7 +93,17 @@ impl fmt::Display for NodeRecord { f.write_str("enode://")?; hex::encode(self.id.as_bytes()).fmt(f)?; f.write_char('@')?; - self.address.fmt(f)?; + match self.address { + IpAddr::V4(ip) => { + ip.fmt(f)?; + } + IpAddr::V6(ip) => { + // encapsulate with brackets + f.write_char('[')?; + ip.fmt(f)?; + f.write_char(']')?; + } + } f.write_char(':')?; self.tcp_port.fmt(f)?; if self.tcp_port != self.udp_port { diff --git a/crates/rpc/rpc-types/src/admin.rs b/crates/rpc/rpc-types/src/admin.rs index bdc57c9b2..7622eb9a9 100644 --- a/crates/rpc/rpc-types/src/admin.rs +++ b/crates/rpc/rpc-types/src/admin.rs @@ -1,16 +1,19 @@ -use reth_network_api::NetworkStatus; +use reth_network_api::{EthProtocolInfo, NetworkStatus}; use reth_primitives::{NodeRecord, PeerId}; use serde::{Deserialize, Serialize}; -use std::net::{IpAddr, SocketAddr}; +use std::{ + collections::BTreeMap, + net::{IpAddr, SocketAddr}, +}; /// Represents the `admin_nodeInfo` response, which can be queried for all the information /// known about the running node at the networking granularity. /// /// Note: this format is not standardized. Reth follows Geth's format, /// see: -#[derive(Serialize, Deserialize, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct NodeInfo { - /// Enode in URL format. + /// Enode of the node in URL format. pub enode: NodeRecord, /// ID of the local node. pub id: PeerId, @@ -21,9 +24,10 @@ pub struct NodeInfo { pub listen_addr: SocketAddr, /// Ports exposed by the node for discovery and listening. pub ports: Ports, + /// Name of the network + pub name: String, /// Networking protocols being run by the local node. - #[serde(flatten)] - pub status: NetworkStatus, + pub protocols: Protocols, } impl NodeInfo { @@ -35,16 +39,42 @@ impl NodeInfo { ip: enr.address, listen_addr: enr.tcp_addr(), ports: Ports { discovery: enr.udp_port, listener: enr.tcp_port }, - status, + name: status.client_version, + protocols: Protocols { eth: status.eth_protocol_info, other: Default::default() }, } } } +/// All supported protocols +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Protocols { + /// Info about `eth` sub-protocol + pub eth: EthProtocolInfo, + /// Placeholder for any other protocols + #[serde(flatten, default)] + pub other: BTreeMap, +} + /// Ports exposed by the node for discovery and listening. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Ports { /// Port exposed for node discovery. pub discovery: u16, /// Port exposed for listening. pub listener: u16, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_node_info_roundtrip() { + let sample = r#"{"enode":"enode://44826a5d6a55f88a18298bca4773fca5749cdc3a5c9f308aa7d810e9b31123f3e7c5fba0b1d70aac5308426f47df2a128a6747040a3815cc7dd7167d03be320d@[::]:30303","id":"44826a5d6a55f88a18298bca4773fca5749cdc3a5c9f308aa7d810e9b31123f3e7c5fba0b1d70aac5308426f47df2a128a6747040a3815cc7dd7167d03be320d","ip":"::","listenAddr":"[::]:30303","name":"reth","ports":{"discovery":30303,"listener":30303},"protocols":{"eth":{"difficulty":17334254859343145000,"genesis":"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3","head":"0xb83f73fbe6220c111136aefd27b160bf4a34085c65ba89f24246b3162257c36a","network":1}}}"#; + + let info: NodeInfo = serde_json::from_str(sample).unwrap(); + let serialized = serde_json::to_string_pretty(&info).unwrap(); + let de_serialized: NodeInfo = serde_json::from_str(&serialized).unwrap(); + assert_eq!(info, de_serialized) + } +}