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
17pub struct AdminApi<N, ChainSpec> {
21 network: N,
23 chain_spec: Arc<ChainSpec>,
25}
26
27impl<N, ChainSpec> AdminApi<N, ChainSpec> {
28 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 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 fn remove_peer(&self, record: AnyNode) -> RpcResult<bool> {
48 self.network.remove_peer(record.peer_id(), PeerKind::Basic);
49 Ok(true)
50 }
51
52 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 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 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 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 macro_rules! set_block_or_time {
126 ($config:expr, [$( $field:ident => $fork:ident,)*]) => {
127 $(
128 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 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}