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