reth_rpc/
admin.rs

1use std::sync::Arc;
2
3use alloy_genesis::ChainConfig;
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, EthereumHardfork, EthereumHardforks, ForkCondition};
11use reth_network_api::{NetworkInfo, Peers};
12use reth_network_peers::{id2pk, 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(record) = record.node_record() {
59            self.network.add_trusted_peer_with_udp(record.id, record.tcp_addr(), record.udp_addr())
60        }
61        self.network.add_trusted_peer_id(record.peer_id());
62        Ok(true)
63    }
64
65    /// Handler for `admin_removeTrustedPeer`
66    fn remove_trusted_peer(&self, record: AnyNode) -> RpcResult<bool> {
67        self.network.remove_peer(record.peer_id(), PeerKind::Trusted);
68        Ok(true)
69    }
70
71    /// Handler for `admin_peers`
72    async fn peers(&self) -> RpcResult<Vec<PeerInfo>> {
73        let peers = self.network.get_all_peers().await.to_rpc_result()?;
74        let mut infos = Vec::with_capacity(peers.len());
75
76        for peer in peers {
77            if let Ok(pk) = id2pk(peer.remote_id) {
78                infos.push(PeerInfo {
79                    id: pk.to_string(),
80                    name: peer.client_version.to_string(),
81                    enode: peer.enode,
82                    enr: peer.enr,
83                    caps: peer
84                        .capabilities
85                        .capabilities()
86                        .iter()
87                        .map(|cap| cap.to_string())
88                        .collect(),
89                    network: PeerNetworkInfo {
90                        remote_address: peer.remote_addr,
91                        local_address: peer.local_addr.unwrap_or_else(|| self.network.local_addr()),
92                        inbound: peer.direction.is_incoming(),
93                        trusted: peer.kind.is_trusted(),
94                        static_node: peer.kind.is_static(),
95                    },
96                    protocols: PeerProtocolInfo {
97                        eth: Some(EthPeerInfo::Info(EthInfo {
98                            version: peer.status.version as u64,
99                        })),
100                        snap: None,
101                        other: Default::default(),
102                    },
103                })
104            }
105        }
106
107        Ok(infos)
108    }
109
110    /// Handler for `admin_nodeInfo`
111    async fn node_info(&self) -> RpcResult<NodeInfo> {
112        let enode = self.network.local_node_record();
113        let status = self.network.network_status().await.to_rpc_result()?;
114        let mut config = ChainConfig {
115            chain_id: self.chain_spec.chain().id(),
116            terminal_total_difficulty_passed: self
117                .chain_spec
118                .final_paris_total_difficulty()
119                .is_some(),
120            terminal_total_difficulty: self
121                .chain_spec
122                .ethereum_fork_activation(EthereumHardfork::Paris)
123                .ttd(),
124            deposit_contract_address: self.chain_spec.deposit_contract().map(|dc| dc.address),
125            ..self.chain_spec.genesis().config.clone()
126        };
127
128        // helper macro to set the block or time for a hardfork if known
129        macro_rules! set_block_or_time {
130            ($config:expr, [$( $field:ident => $fork:ident,)*]) => {
131                $(
132                    // don't overwrite if already set
133                    if $config.$field.is_none() {
134                        $config.$field = match self.chain_spec.ethereum_fork_activation(EthereumHardfork::$fork) {
135                            ForkCondition::Block(block) => Some(block),
136                            ForkCondition::TTD { fork_block, .. } => fork_block,
137                            ForkCondition::Timestamp(ts) => Some(ts),
138                            ForkCondition::Never => None,
139                        };
140                    }
141                )*
142            };
143        }
144
145        set_block_or_time!(config, [
146            homestead_block => Homestead,
147            dao_fork_block => Dao,
148            eip150_block => Tangerine,
149            eip155_block => SpuriousDragon,
150            eip158_block => SpuriousDragon,
151            byzantium_block => Byzantium,
152            constantinople_block => Constantinople,
153            petersburg_block => Petersburg,
154            istanbul_block => Istanbul,
155            muir_glacier_block => MuirGlacier,
156            berlin_block => Berlin,
157            london_block => London,
158            arrow_glacier_block => ArrowGlacier,
159            gray_glacier_block => GrayGlacier,
160            shanghai_time => Shanghai,
161            cancun_time => Cancun,
162            prague_time => Prague,
163        ]);
164
165        Ok(NodeInfo {
166            id: id2pk(enode.id)
167                .map(|pk| pk.to_string())
168                .unwrap_or_else(|_| alloy_primitives::hex::encode(enode.id.as_slice())),
169            name: status.client_version,
170            enode: enode.to_string(),
171            enr: self.network.local_enr().to_string(),
172            ip: enode.address,
173            ports: Ports { discovery: enode.udp_port, listener: enode.tcp_port },
174            listen_addr: enode.tcp_addr(),
175            #[expect(deprecated)]
176            protocols: ProtocolInfo {
177                eth: Some(EthProtocolInfo {
178                    network: status.eth_protocol_info.network,
179                    genesis: status.eth_protocol_info.genesis,
180                    config,
181                    head: status.eth_protocol_info.head,
182                    difficulty: None,
183                }),
184                snap: None,
185            },
186        })
187    }
188
189    /// Handler for `admin_peerEvents`
190    async fn subscribe_peer_events(
191        &self,
192        _pending: jsonrpsee::PendingSubscriptionSink,
193    ) -> jsonrpsee::core::SubscriptionResult {
194        Err("admin_peerEvents is not implemented yet".into())
195    }
196
197    /// Handler for `admin_clearTxpool`
198    async fn clear_txpool(&self) -> RpcResult<u64> {
199        let all_hashes = self.pool.all_transaction_hashes();
200        let count = all_hashes.len() as u64;
201        let _ = self.pool.remove_transactions(all_hashes);
202        Ok(count)
203    }
204}
205
206impl<N, ChainSpec, Pool> std::fmt::Debug for AdminApi<N, ChainSpec, Pool> {
207    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208        f.debug_struct("AdminApi").finish_non_exhaustive()
209    }
210}