Skip to main content

reth_rpc/
admin.rs

1use std::sync::Arc;
2
3use alloy_primitives::keccak256;
4use alloy_rpc_types_admin::{
5    EthInfo, EthPeerInfo, EthProtocolInfo, NodeInfo, PeerInfo, PeerNetworkInfo, PeerProtocolInfo,
6    Ports, ProtocolInfo,
7};
8use jsonrpsee::core::RpcResult;
9use reth_chainspec::{EthChainSpec, EthereumHardforks};
10use reth_network_api::{NetworkInfo, Peers};
11use reth_network_peers::{AnyNode, NodeRecord};
12use reth_network_types::PeerKind;
13use reth_rpc_api::AdminApiServer;
14use reth_rpc_server_types::ToRpcResult;
15use reth_transaction_pool::TransactionPool;
16
17/// `admin` API implementation.
18///
19/// This type provides the functionality for handling `admin` related requests.
20pub struct AdminApi<N, ChainSpec, Pool> {
21    /// An interface to interact with the network
22    network: N,
23    /// The specification of the blockchain's configuration.
24    chain_spec: Arc<ChainSpec>,
25    /// The transaction pool
26    pool: Pool,
27}
28
29impl<N, ChainSpec, Pool> AdminApi<N, ChainSpec, Pool> {
30    /// Creates a new instance of `AdminApi`.
31    pub const fn new(network: N, chain_spec: Arc<ChainSpec>, pool: Pool) -> Self {
32        Self { network, chain_spec, pool }
33    }
34}
35
36impl<N, ChainSpec, Pool> AdminApiServer for AdminApi<N, ChainSpec, Pool>
37where
38    N: NetworkInfo + Peers + 'static,
39    ChainSpec: EthChainSpec + EthereumHardforks + 'static,
40    Pool: TransactionPool + 'static,
41{
42    /// Handler for `admin_addPeer`
43    fn add_peer(&self, record: NodeRecord) -> RpcResult<bool> {
44        self.network.add_peer_with_udp(record.id, record.tcp_addr(), record.udp_addr());
45        Ok(true)
46    }
47
48    /// Handler for `admin_removePeer`
49    fn remove_peer(&self, record: AnyNode) -> RpcResult<bool> {
50        self.network.remove_peer(record.peer_id(), PeerKind::Basic);
51        Ok(true)
52    }
53
54    /// Handler for `admin_addTrustedPeer`
55    fn add_trusted_peer(&self, record: AnyNode) -> RpcResult<bool> {
56        if let Some(trusted) = record.trusted_peer().cloned() {
57            self.network.add_trusted_peer_node(trusted);
58        } else {
59            if let Some(record) = record.node_record() {
60                self.network.add_trusted_peer_with_udp(
61                    record.id,
62                    record.tcp_addr(),
63                    record.udp_addr(),
64                )
65            }
66            self.network.add_trusted_peer_id(record.peer_id());
67        }
68        Ok(true)
69    }
70
71    /// Handler for `admin_removeTrustedPeer`
72    fn remove_trusted_peer(&self, record: AnyNode) -> RpcResult<bool> {
73        self.network.remove_peer(record.peer_id(), PeerKind::Trusted);
74        Ok(true)
75    }
76
77    /// Handler for `admin_banPeer`
78    fn ban_peer(&self, record: AnyNode) -> RpcResult<bool> {
79        self.network.ban_peer(record.peer_id());
80        Ok(true)
81    }
82
83    /// Handler for `admin_unbanPeer`
84    fn unban_peer(&self, record: AnyNode) -> RpcResult<bool> {
85        self.network.unban_peer(record.peer_id());
86        Ok(true)
87    }
88
89    /// Handler for `admin_peers`
90    async fn peers(&self) -> RpcResult<Vec<PeerInfo>> {
91        let peers = self.network.get_all_peers().await.to_rpc_result()?;
92        let mut infos = Vec::with_capacity(peers.len());
93
94        for peer in peers {
95            infos.push(PeerInfo {
96                id: alloy_primitives::hex::encode(keccak256(peer.remote_id.as_slice())),
97                name: peer.client_version.to_string(),
98                enode: peer.enode,
99                enr: peer.enr,
100                caps: peer.capabilities.capabilities().iter().map(|cap| cap.to_string()).collect(),
101                network: PeerNetworkInfo {
102                    remote_address: peer.remote_addr,
103                    local_address: peer.local_addr.unwrap_or_else(|| self.network.local_addr()),
104                    inbound: peer.direction.is_incoming(),
105                    trusted: peer.kind.is_trusted(),
106                    static_node: peer.kind.is_static(),
107                },
108                protocols: PeerProtocolInfo {
109                    eth: Some(EthPeerInfo::Info(EthInfo { version: peer.status.version as u64 })),
110                    snap: None,
111                    other: Default::default(),
112                },
113            })
114        }
115
116        Ok(infos)
117    }
118
119    /// Handler for `admin_nodeInfo`
120    async fn node_info(&self) -> RpcResult<NodeInfo> {
121        let enode = self.network.local_node_record();
122        let status = self.network.network_status().await.to_rpc_result()?;
123        let config = crate::chain_config::chain_config(&*self.chain_spec);
124
125        Ok(NodeInfo {
126            id: alloy_primitives::hex::encode(keccak256(enode.id.as_slice())),
127            name: status.client_version,
128            enode: enode.to_string(),
129            enr: self.network.local_enr().to_string(),
130            ip: enode.address,
131            ports: Ports { discovery: enode.udp_port, listener: enode.tcp_port },
132            listen_addr: enode.tcp_addr(),
133            #[expect(deprecated)]
134            protocols: ProtocolInfo {
135                eth: Some(EthProtocolInfo {
136                    network: status.eth_protocol_info.network,
137                    genesis: status.eth_protocol_info.genesis,
138                    config,
139                    head: status.eth_protocol_info.head,
140                    difficulty: None,
141                }),
142                snap: None,
143            },
144        })
145    }
146
147    /// Handler for `admin_peerEvents`
148    async fn subscribe_peer_events(
149        &self,
150        _pending: jsonrpsee::PendingSubscriptionSink,
151    ) -> jsonrpsee::core::SubscriptionResult {
152        Err("admin_peerEvents is not implemented yet".into())
153    }
154
155    /// Handler for `admin_clearTxpool`
156    async fn clear_txpool(&self) -> RpcResult<u64> {
157        let all_hashes = self.pool.all_transaction_hashes();
158        let count = all_hashes.len() as u64;
159        let _ = self.pool.remove_transactions(all_hashes);
160        Ok(count)
161    }
162}
163
164impl<N, ChainSpec, Pool> std::fmt::Debug for AdminApi<N, ChainSpec, Pool> {
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        f.debug_struct("AdminApi").finish_non_exhaustive()
167    }
168}