fix(rpc): make NodeInfo behave as geth nodeInfo (#1200)

This commit is contained in:
Matthias Seitz
2023-02-06 22:12:02 +01:00
committed by GitHub
parent 02a6aec3fb
commit b3de7adae7
5 changed files with 74 additions and 14 deletions

View File

@ -86,7 +86,7 @@ pub enum PeerKind {
} }
/// The status of the network being ran by the local node. /// 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 { pub struct NetworkStatus {
/// The local node client version. /// The local node client version.
pub client_version: String, pub client_version: String,
@ -95,11 +95,11 @@ pub struct NetworkStatus {
/// Information about the Ethereum Wire Protocol. /// Information about the Ethereum Wire Protocol.
pub eth_protocol_info: EthProtocolInfo, pub eth_protocol_info: EthProtocolInfo,
} }
/// Information about the Ethereum Wire Protocol (ETH) /// Information about the Ethereum Wire Protocol (ETH)
#[derive(Serialize, Deserialize, Debug, Default)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct EthProtocolInfo { pub struct EthProtocolInfo {
/// The current difficulty at the head of the chain. /// The current difficulty at the head of the chain.
#[serde(deserialize_with = "reth_primitives::serde_helper::deserialize_json_u256")]
pub difficulty: U256, pub difficulty: U256,
/// The block hash of the head of the chain. /// The block hash of the head of the chain.
pub head: H256, pub head: H256,

View File

@ -9,6 +9,12 @@ use std::{fmt, str::FromStr};
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] #[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct JsonU256(pub U256); pub struct JsonU256(pub U256);
impl From<JsonU256> for U256 {
fn from(value: JsonU256) -> Self {
value.0
}
}
impl Serialize for JsonU256 { impl Serialize for JsonU256 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where 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<U256, D::Error>
where
D: Deserializer<'de>,
{
let num = JsonU256::deserialize(deserializer)?;
Ok(num.into())
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::JsonU256; use super::JsonU256;

View File

@ -96,13 +96,18 @@ mod __reexport {
pub use tiny_keccak; pub use tiny_keccak;
} }
// Useful reexports
pub use __reexport::*;
/// Various utilities /// Various utilities
pub mod utils { pub mod utils {
pub use ethers_core::types::serde_helpers; pub use ethers_core::types::serde_helpers;
} }
// Useful reexports /// Helpers for working with serde
pub use __reexport::*; pub mod serde_helper {
pub use crate::jsonu256::deserialize_json_u256;
}
/// Returns the keccak256 hash for the given data. /// Returns the keccak256 hash for the given data.
#[inline] #[inline]

View File

@ -93,7 +93,17 @@ impl fmt::Display for NodeRecord {
f.write_str("enode://")?; f.write_str("enode://")?;
hex::encode(self.id.as_bytes()).fmt(f)?; hex::encode(self.id.as_bytes()).fmt(f)?;
f.write_char('@')?; 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(':')?; f.write_char(':')?;
self.tcp_port.fmt(f)?; self.tcp_port.fmt(f)?;
if self.tcp_port != self.udp_port { if self.tcp_port != self.udp_port {

View File

@ -1,16 +1,19 @@
use reth_network_api::NetworkStatus; use reth_network_api::{EthProtocolInfo, NetworkStatus};
use reth_primitives::{NodeRecord, PeerId}; use reth_primitives::{NodeRecord, PeerId};
use serde::{Deserialize, Serialize}; 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 /// Represents the `admin_nodeInfo` response, which can be queried for all the information
/// known about the running node at the networking granularity. /// known about the running node at the networking granularity.
/// ///
/// Note: this format is not standardized. Reth follows Geth's format, /// Note: this format is not standardized. Reth follows Geth's format,
/// see: <https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-admin> /// see: <https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-admin>
#[derive(Serialize, Deserialize, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct NodeInfo { pub struct NodeInfo {
/// Enode in URL format. /// Enode of the node in URL format.
pub enode: NodeRecord, pub enode: NodeRecord,
/// ID of the local node. /// ID of the local node.
pub id: PeerId, pub id: PeerId,
@ -21,9 +24,10 @@ pub struct NodeInfo {
pub listen_addr: SocketAddr, pub listen_addr: SocketAddr,
/// Ports exposed by the node for discovery and listening. /// Ports exposed by the node for discovery and listening.
pub ports: Ports, pub ports: Ports,
/// Name of the network
pub name: String,
/// Networking protocols being run by the local node. /// Networking protocols being run by the local node.
#[serde(flatten)] pub protocols: Protocols,
pub status: NetworkStatus,
} }
impl NodeInfo { impl NodeInfo {
@ -35,16 +39,42 @@ impl NodeInfo {
ip: enr.address, ip: enr.address,
listen_addr: enr.tcp_addr(), listen_addr: enr.tcp_addr(),
ports: Ports { discovery: enr.udp_port, listener: enr.tcp_port }, 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<String, serde_json::Value>,
}
/// Ports exposed by the node for discovery and listening. /// Ports exposed by the node for discovery and listening.
#[derive(Serialize, Deserialize, Debug)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Ports { pub struct Ports {
/// Port exposed for node discovery. /// Port exposed for node discovery.
pub discovery: u16, pub discovery: u16,
/// Port exposed for listening. /// Port exposed for listening.
pub listener: u16, 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)
}
}