diff --git a/ethers-providers/src/admin.rs b/ethers-providers/src/admin.rs index 718a63be73..d117cc34fc 100644 --- a/ethers-providers/src/admin.rs +++ b/ethers-providers/src/admin.rs @@ -8,46 +8,52 @@ use std::net::{IpAddr, SocketAddr}; /// details. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct NodeInfo { + // TODO: double check that this is a public key /// The node's secp256k1 public key. pub id: H256, - /// The client user agent, containing a client name, version, OS, and other metadata. + /// The node's user agent, containing a client name, version, OS, and other metadata. pub name: String, - /// The enode URL of the running client. + /// The enode URL of the connected node. pub enode: String, /// The [ENR](https://eips.ethereum.org/EIPS/eip-778) of the running client. pub enr: Enr, - /// The IP address of the running client. + /// The IP address of the connected node. pub ip: IpAddr, - /// The client's listening ports. + /// The node's listening ports. pub ports: Ports, - /// The client's listening address. + /// The node's listening address. #[serde(rename = "listenAddr")] pub listen_addr: String, - /// The protocols that the client supports, with protocol metadata. + /// The protocols that the node supports, with protocol metadata. pub protocols: ProtocolInfo, } /// Represents a node's discovery and listener ports. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Ports { - /// The discovery port of the running client. + /// The node's discovery port. pub discovery: u16, - /// The listener port of the running client. + /// The node's listener port. pub listener: u16, } -/// Represents the protocols that the client supports. +/// Represents protocols that the connected RPC node supports. +/// +/// This contains protocol information reported by the connected RPC node. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ProtocolInfo { + #[serde(default, skip_serializing_if = "Option::is_none")] pub eth: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] pub snap: Option, } @@ -58,7 +64,7 @@ pub struct ProtocolInfo { /// for how these fields are determined. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct EthProtocolInfo { - /// The ethereum network ID. + /// The eth network version. pub network: u64, /// The total difficulty of the host's blockchain. @@ -75,27 +81,71 @@ pub struct EthProtocolInfo { pub head: H256, } -/// Represents a short summary of the `snap` sub-protocol metadata known about the host peer. +/// Represents a short summary of the host's `snap` sub-protocol metadata. /// /// This is just an empty struct, because [geth's internal representation is /// empty](https://github.com/ethereum/go-ethereum/blob/c2e0abce2eedc1ba2a1b32c46fd07ef18a25354a/eth/protocols/snap/handler.go#L571-L576). #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SnapProtocolInfo {} +/// Represents the protocols that a peer supports. +/// +/// This differs from [`ProtocolInfo`] in that [`PeerProtocolInfo`] contains protocol information +/// gathered from the protocol handshake, and [`ProtocolInfo`] contains information reported by the +/// connected RPC node. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct PeerProtocolInfo { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub eth: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub snap: Option, +} + +/// Represents a short summary of the `eth` sub-protocol metadata known about a connected peer +/// +/// See [geth's `ethPeerInfo` +/// struct](https://github.com/ethereum/go-ethereum/blob/53d1ae096ac0515173e17f0f81a553e5f39027f7/eth/peer.go#L28) +/// for how these fields are determined. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct EthPeerInfo { + /// The negotiated eth version. + pub version: u64, + + /// The total difficulty of the peer's blockchain. + #[serde(deserialize_with = "from_int_or_hex")] + pub difficulty: U256, + + /// The hash of the peer's best known block. + pub head: H256, +} + +/// Represents a short summary of the `snap` sub-protocol metadata known about a connected peer. +/// +/// See [geth's `snapPeerInfo` +/// struct](https://github.com/ethereum/go-ethereum/blob/53d1ae096ac0515173e17f0f81a553e5f39027f7/eth/peer.go#L53) +/// for how these fields are determined. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct SnapPeerInfo { + /// The negotiated snap version. + pub version: u64, +} + /// Represents a short summary of information known about a connected peer. /// /// See [geth's `PeerInfo` struct](https://github.com/ethereum/go-ethereum/blob/64dccf7aa411c5c7cd36090c3d9b9892945ae813/p2p/peer.go#L484) for the source of each field. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PeerInfo { /// The peer's ENR. - #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] pub enr: Option>, /// The peer's enode URL. pub enode: String, - /// The peer's network ID. - pub id: String, + // TODO: unify comment with the one in `NodeInfo` + /// The peer's enode ID. + pub id: H256, /// The peer's name. pub name: String, @@ -107,7 +157,7 @@ pub struct PeerInfo { pub network: PeerNetworkInfo, /// The protocols that the peer supports, with protocol metadata. - pub protocols: ProtocolInfo, + pub protocols: PeerProtocolInfo, } /// Represents networking related information about the peer, including details about whether or @@ -131,3 +181,68 @@ pub struct PeerNetworkInfo { #[serde(rename = "static")] pub static_node: bool, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn deserialize_peer_info_version() { + let response = r#"{ + "enode":"enode://bb37b7302f79e47c1226d6e3ccf0ef6d51146019efdcc1f6e861fd1c1a78d5e84e486225a6a8a503b93d5c50125ee980835c92bde7f7d12f074c16f4e439a578@127.0.0.1:60872", + "id":"ca23c04b7e796da5d6a5f04a62b81c88d41b1341537db85a2b6443e838d8339b", + "name":"Geth/v1.10.19-stable/darwin-arm64/go1.18.3", + "caps":["eth/66","eth/67","snap/1"], + "network":{ + "localAddress":"127.0.0.1:30304", + "remoteAddress":"127.0.0.1:60872", + "inbound":true, + "trusted":false, + "static":false + }, + "protocols":{ + "eth":{ + "version":67, + "difficulty":0, + "head":"0xb04009ddf4b0763f42778e7d5937e49bebf1e11b2d26c9dac6cefb5f84b6f8ea" + }, + "snap":{"version":1} + } + }"#; + let peer_info: PeerInfo = serde_json::from_str(response).unwrap(); + + assert_eq!(peer_info.enode, "enode://bb37b7302f79e47c1226d6e3ccf0ef6d51146019efdcc1f6e861fd1c1a78d5e84e486225a6a8a503b93d5c50125ee980835c92bde7f7d12f074c16f4e439a578@127.0.0.1:60872"); + } + + #[test] + fn deserialize_node_info_network() { + // this response also has an enr + let response = r#"{ + "id":"6e2fe698f3064cd99410926ce16734e35e3cc947d4354461d2594f2d2dd9f7b6", + "name":"Geth/v1.10.19-stable/darwin-arm64/go1.18.3", + "enode":"enode://d7dfaea49c7ef37701e668652bcf1bc63d3abb2ae97593374a949e175e4ff128730a2f35199f3462a56298b981dfc395a5abebd2d6f0284ffe5bdc3d8e258b86@24.147.252.56:30304?discport=0", + "enr":"enr:-Jy4QIvS0dKBLjTTV_RojS8hjriwWsJNHRVyOh4Pk4aUXc5SZjKRVIOeYc7BqzEmbCjLdIY4Ln7x5ZPf-2SsBAc2_zqGAYSwY1zog2V0aMfGhNegsXuAgmlkgnY0gmlwhBiT_DiJc2VjcDI1NmsxoQLX366knH7zdwHmaGUrzxvGPTq7Kul1kzdKlJ4XXk_xKIRzbmFwwIN0Y3CCdmA", + "ip":"24.147.252.56", + "ports":{ + "discovery":0, + "listener":30304 + }, + "listenAddr":"[::]:30304", + "protocols":{ + "eth":{ + "network":1337, + "difficulty":0, + "genesis":"0xb04009ddf4b0763f42778e7d5937e49bebf1e11b2d26c9dac6cefb5f84b6f8ea", + "config":{ + "chainId":0, + "eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "head":"0xb04009ddf4b0763f42778e7d5937e49bebf1e11b2d26c9dac6cefb5f84b6f8ea" + }, + "snap":{} + } + }"#; + + let _: NodeInfo = serde_json::from_str(response).unwrap(); + } +} diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index 259f490256..98249a250a 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -2293,11 +2293,11 @@ mod tests { // check that the second peer is in the list (it uses an enr so the enr should be Some) assert_eq!(peers.len(), 1); - let first_peer = peers.get(0).unwrap(); - assert_eq!(first_peer.enr, Some(second_info.enr)); + let peer = peers.get(0).unwrap(); + assert_eq!(peer.id, second_info.id); // remove directories - // std::fs::remove_dir_all(dir1).unwrap(); - // std::fs::remove_dir_all(dir2).unwrap(); + std::fs::remove_dir_all(dir1).unwrap(); + std::fs::remove_dir_all(dir2).unwrap(); } }