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