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