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