Skip to main content

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::{AnyNode, NodeRecord};
13use reth_network_types::PeerKind;
14use reth_rpc_api::AdminApiServer;
15use reth_rpc_server_types::ToRpcResult;
16use reth_transaction_pool::TransactionPool;
17use revm_primitives::keccak256;
18
19/// `admin` API implementation.
20///
21/// This type provides the functionality for handling `admin` related requests.
22pub struct AdminApi<N, ChainSpec, Pool> {
23    /// An interface to interact with the network
24    network: N,
25    /// The specification of the blockchain's configuration.
26    chain_spec: Arc<ChainSpec>,
27    /// The transaction pool
28    pool: Pool,
29}
30
31impl<N, ChainSpec, Pool> AdminApi<N, ChainSpec, Pool> {
32    /// Creates a new instance of `AdminApi`.
33    pub const fn new(network: N, chain_spec: Arc<ChainSpec>, pool: Pool) -> Self {
34        Self { network, chain_spec, pool }
35    }
36}
37
38#[async_trait]
39impl<N, ChainSpec, Pool> AdminApiServer for AdminApi<N, ChainSpec, Pool>
40where
41    N: NetworkInfo + Peers + 'static,
42    ChainSpec: EthChainSpec + EthereumHardforks + Send + Sync + 'static,
43    Pool: TransactionPool + 'static,
44{
45    /// Handler for `admin_addPeer`
46    fn add_peer(&self, record: NodeRecord) -> RpcResult<bool> {
47        self.network.add_peer_with_udp(record.id, record.tcp_addr(), record.udp_addr());
48        Ok(true)
49    }
50
51    /// Handler for `admin_removePeer`
52    fn remove_peer(&self, record: AnyNode) -> RpcResult<bool> {
53        self.network.remove_peer(record.peer_id(), PeerKind::Basic);
54        Ok(true)
55    }
56
57    /// Handler for `admin_addTrustedPeer`
58    fn add_trusted_peer(&self, record: AnyNode) -> RpcResult<bool> {
59        if let Some(trusted) = record.trusted_peer().cloned() {
60            self.network.add_trusted_peer_node(trusted);
61        } else {
62            if let Some(record) = record.node_record() {
63                self.network.add_trusted_peer_with_udp(
64                    record.id,
65                    record.tcp_addr(),
66                    record.udp_addr(),
67                )
68            }
69            self.network.add_trusted_peer_id(record.peer_id());
70        }
71        Ok(true)
72    }
73
74    /// Handler for `admin_removeTrustedPeer`
75    fn remove_trusted_peer(&self, record: AnyNode) -> RpcResult<bool> {
76        self.network.remove_peer(record.peer_id(), PeerKind::Trusted);
77        Ok(true)
78    }
79
80    /// Handler for `admin_banPeer`
81    fn ban_peer(&self, record: AnyNode) -> RpcResult<bool> {
82        self.network.ban_peer(record.peer_id());
83        Ok(true)
84    }
85
86    /// Handler for `admin_unbanPeer`
87    fn unban_peer(&self, record: AnyNode) -> RpcResult<bool> {
88        self.network.unban_peer(record.peer_id());
89        Ok(true)
90    }
91
92    /// Handler for `admin_peers`
93    async fn peers(&self) -> RpcResult<Vec<PeerInfo>> {
94        let peers = self.network.get_all_peers().await.to_rpc_result()?;
95        let mut infos = Vec::with_capacity(peers.len());
96
97        for peer in peers {
98            infos.push(PeerInfo {
99                id: alloy_primitives::hex::encode(keccak256(peer.remote_id.as_slice())),
100                name: peer.client_version.to_string(),
101                enode: peer.enode,
102                enr: peer.enr,
103                caps: peer.capabilities.capabilities().iter().map(|cap| cap.to_string()).collect(),
104                network: PeerNetworkInfo {
105                    remote_address: peer.remote_addr,
106                    local_address: peer.local_addr.unwrap_or_else(|| self.network.local_addr()),
107                    inbound: peer.direction.is_incoming(),
108                    trusted: peer.kind.is_trusted(),
109                    static_node: peer.kind.is_static(),
110                },
111                protocols: PeerProtocolInfo {
112                    eth: Some(EthPeerInfo::Info(EthInfo { version: peer.status.version as u64 })),
113                    snap: None,
114                    other: Default::default(),
115                },
116            })
117        }
118
119        Ok(infos)
120    }
121
122    /// Handler for `admin_nodeInfo`
123    async fn node_info(&self) -> RpcResult<NodeInfo> {
124        let enode = self.network.local_node_record();
125        let status = self.network.network_status().await.to_rpc_result()?;
126        let mut config = ChainConfig {
127            chain_id: self.chain_spec.chain().id(),
128            terminal_total_difficulty_passed: self
129                .chain_spec
130                .final_paris_total_difficulty()
131                .is_some(),
132            terminal_total_difficulty: self
133                .chain_spec
134                .ethereum_fork_activation(EthereumHardfork::Paris)
135                .ttd(),
136            deposit_contract_address: self.chain_spec.deposit_contract().map(|dc| dc.address),
137            ..self.chain_spec.genesis().config.clone()
138        };
139
140        // helper macro to set the block or time for a hardfork if known
141        macro_rules! set_block_or_time {
142            ($config:expr, [$( $field:ident => $fork:ident,)*]) => {
143                $(
144                    // don't overwrite if already set
145                    if $config.$field.is_none() {
146                        $config.$field = match self.chain_spec.ethereum_fork_activation(EthereumHardfork::$fork) {
147                            ForkCondition::Block(block) => Some(block),
148                            ForkCondition::TTD { fork_block, .. } => fork_block,
149                            ForkCondition::Timestamp(ts) => Some(ts),
150                            ForkCondition::Never => None,
151                        };
152                    }
153                )*
154            };
155        }
156
157        set_block_or_time!(config, [
158            homestead_block => Homestead,
159            dao_fork_block => Dao,
160            eip150_block => Tangerine,
161            eip155_block => SpuriousDragon,
162            eip158_block => SpuriousDragon,
163            byzantium_block => Byzantium,
164            constantinople_block => Constantinople,
165            petersburg_block => Petersburg,
166            istanbul_block => Istanbul,
167            muir_glacier_block => MuirGlacier,
168            berlin_block => Berlin,
169            london_block => London,
170            arrow_glacier_block => ArrowGlacier,
171            gray_glacier_block => GrayGlacier,
172            shanghai_time => Shanghai,
173            cancun_time => Cancun,
174            prague_time => Prague,
175        ]);
176
177        Ok(NodeInfo {
178            id: alloy_primitives::hex::encode(keccak256(enode.id.as_slice())),
179            name: status.client_version,
180            enode: enode.to_string(),
181            enr: self.network.local_enr().to_string(),
182            ip: enode.address,
183            ports: Ports { discovery: enode.udp_port, listener: enode.tcp_port },
184            listen_addr: enode.tcp_addr(),
185            #[expect(deprecated)]
186            protocols: ProtocolInfo {
187                eth: Some(EthProtocolInfo {
188                    network: status.eth_protocol_info.network,
189                    genesis: status.eth_protocol_info.genesis,
190                    config,
191                    head: status.eth_protocol_info.head,
192                    difficulty: None,
193                }),
194                snap: None,
195            },
196        })
197    }
198
199    /// Handler for `admin_peerEvents`
200    async fn subscribe_peer_events(
201        &self,
202        _pending: jsonrpsee::PendingSubscriptionSink,
203    ) -> jsonrpsee::core::SubscriptionResult {
204        Err("admin_peerEvents is not implemented yet".into())
205    }
206
207    /// Handler for `admin_clearTxpool`
208    async fn clear_txpool(&self) -> RpcResult<u64> {
209        let all_hashes = self.pool.all_transaction_hashes();
210        let count = all_hashes.len() as u64;
211        let _ = self.pool.remove_transactions(all_hashes);
212        Ok(count)
213    }
214}
215
216impl<N, ChainSpec, Pool> std::fmt::Debug for AdminApi<N, ChainSpec, Pool> {
217    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218        f.debug_struct("AdminApi").finish_non_exhaustive()
219    }
220}