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