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