alloy_provider/ext/
admin.rs1use crate::Provider;
3use alloy_network::Network;
4use alloy_rpc_types_admin::{NodeInfo, PeerInfo};
5use alloy_transport::TransportResult;
6
7#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
9#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
10pub trait AdminApi<N>: Send + Sync {
11 async fn add_peer(&self, record: &str) -> TransportResult<bool>;
14
15 async fn add_trusted_peer(&self, record: &str) -> TransportResult<bool>;
18
19 async fn remove_peer(&self, record: &str) -> TransportResult<bool>;
22
23 async fn remove_trusted_peer(&self, record: &str) -> TransportResult<bool>;
27
28 async fn peers(&self) -> TransportResult<Vec<PeerInfo>>;
30
31 async fn node_info(&self) -> TransportResult<NodeInfo>;
34
35 #[cfg(feature = "pubsub")]
37 async fn subscribe_peer_events(
38 &self,
39 ) -> TransportResult<alloy_pubsub::Subscription<alloy_rpc_types_admin::PeerEvent>>;
40}
41
42#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
43#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
44impl<N, P> AdminApi<N> for P
45where
46 N: Network,
47 P: Provider<N>,
48{
49 async fn add_peer(&self, record: &str) -> TransportResult<bool> {
50 self.client().request("admin_addPeer", (record,)).await
51 }
52
53 async fn add_trusted_peer(&self, record: &str) -> TransportResult<bool> {
54 self.client().request("admin_addTrustedPeer", (record,)).await
55 }
56
57 async fn remove_peer(&self, record: &str) -> TransportResult<bool> {
58 self.client().request("admin_removePeer", (record,)).await
59 }
60
61 async fn remove_trusted_peer(&self, record: &str) -> TransportResult<bool> {
62 self.client().request("admin_removeTrustedPeer", (record,)).await
63 }
64
65 async fn peers(&self) -> TransportResult<Vec<PeerInfo>> {
66 self.client().request_noparams("admin_peers").await
67 }
68
69 async fn node_info(&self) -> TransportResult<NodeInfo> {
70 self.client().request_noparams("admin_nodeInfo").await
71 }
72
73 #[cfg(feature = "pubsub")]
74 async fn subscribe_peer_events(
75 &self,
76 ) -> TransportResult<alloy_pubsub::Subscription<alloy_rpc_types_admin::PeerEvent>> {
77 self.root().pubsub_frontend()?;
78 let mut call = self.client().request_noparams("admin_peerEvents_subscribe");
79 call.set_is_subscription();
80 let id = call.await?;
81 self.root().get_subscription(id).await
82 }
83}
84
85#[cfg(test)]
86mod test {
87 use super::*;
88 use crate::{ext::test::async_ci_only, ProviderBuilder};
89 use alloy_node_bindings::{utils::run_with_tempdir, Geth};
90
91 #[tokio::test]
92 async fn node_info() {
93 async_ci_only(|| async move {
94 run_with_tempdir("geth-test-", |temp_dir| async move {
95 let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
96 let provider = ProviderBuilder::new().on_http(geth.endpoint_url());
97 let node_info = provider.node_info().await.unwrap();
98 assert!(node_info.enode.starts_with("enode://"));
99 })
100 .await;
101 })
102 .await;
103 }
104
105 #[tokio::test]
106 async fn admin_peers() {
107 async_ci_only(|| async move {
108 run_with_tempdir("geth-test-1", |temp_dir_1| async move {
109 run_with_tempdir("geth-test-2", |temp_dir_2| async move {
110 let geth1 = Geth::new().disable_discovery().data_dir(&temp_dir_1).spawn();
111 let mut geth2 =
112 Geth::new().disable_discovery().port(0u16).data_dir(&temp_dir_2).spawn();
113
114 let provider1 = ProviderBuilder::new().on_http(geth1.endpoint_url());
115 let provider2 = ProviderBuilder::new().on_http(geth2.endpoint_url());
116 let node1_info = provider1.node_info().await.unwrap();
117 let node1_id = node1_info.id;
118 let node1_enode = node1_info.enode;
119
120 let added = provider2.add_peer(&node1_enode).await.unwrap();
121 assert!(added);
122 geth2.wait_to_add_peer(&node1_id).unwrap();
123 let peers = provider2.peers().await.unwrap();
124 assert_eq!(peers[0].enode, node1_enode);
125 })
126 .await;
127 })
128 .await;
129 })
130 .await;
131 }
132}