alloy_provider/ext/
admin.rs1#[cfg(feature = "pubsub")]
3use crate::GetSubscription;
4use crate::Provider;
5use alloy_network::Network;
6use alloy_rpc_types_admin::{NodeInfo, PeerInfo};
7use alloy_transport::TransportResult;
8
9#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
11#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
12pub trait AdminApi<N>: Send + Sync {
13 async fn add_peer(&self, record: &str) -> TransportResult<bool>;
16
17 async fn add_trusted_peer(&self, record: &str) -> TransportResult<bool>;
20
21 async fn remove_peer(&self, record: &str) -> TransportResult<bool>;
24
25 async fn remove_trusted_peer(&self, record: &str) -> TransportResult<bool>;
29
30 async fn peers(&self) -> TransportResult<Vec<PeerInfo>>;
32
33 async fn node_info(&self) -> TransportResult<NodeInfo>;
36
37 #[cfg(feature = "pubsub")]
39 fn subscribe_peer_events(
40 &self,
41 ) -> GetSubscription<alloy_rpc_client::NoParams, alloy_rpc_types_admin::PeerEvent>;
42}
43
44#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
45#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
46impl<N, P> AdminApi<N> for P
47where
48 N: Network,
49 P: Provider<N>,
50{
51 async fn add_peer(&self, record: &str) -> TransportResult<bool> {
52 self.client().request("admin_addPeer", (record,)).await
53 }
54
55 async fn add_trusted_peer(&self, record: &str) -> TransportResult<bool> {
56 self.client().request("admin_addTrustedPeer", (record,)).await
57 }
58
59 async fn remove_peer(&self, record: &str) -> TransportResult<bool> {
60 self.client().request("admin_removePeer", (record,)).await
61 }
62
63 async fn remove_trusted_peer(&self, record: &str) -> TransportResult<bool> {
64 self.client().request("admin_removeTrustedPeer", (record,)).await
65 }
66
67 async fn peers(&self) -> TransportResult<Vec<PeerInfo>> {
68 self.client().request_noparams("admin_peers").await
69 }
70
71 async fn node_info(&self) -> TransportResult<NodeInfo> {
72 self.client().request_noparams("admin_nodeInfo").await
73 }
74
75 #[cfg(feature = "pubsub")]
76 fn subscribe_peer_events(
77 &self,
78 ) -> GetSubscription<alloy_rpc_client::NoParams, alloy_rpc_types_admin::PeerEvent> {
79 let mut rpc_call = self.client().request_noparams("admin_peerEvents_subscribe");
80 rpc_call.set_is_subscription();
81 GetSubscription::new(self.weak_client(), rpc_call)
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().connect_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 =
111 Geth::new().disable_discovery().keep_stderr().data_dir(&temp_dir_1).spawn();
112 let mut geth2 = Geth::new()
113 .disable_discovery()
114 .keep_stderr()
115 .port(0u16)
116 .data_dir(&temp_dir_2)
117 .spawn();
118
119 let provider1 = ProviderBuilder::new().connect_http(geth1.endpoint_url());
120 let provider2 = ProviderBuilder::new().connect_http(geth2.endpoint_url());
121 let node1_info = provider1.node_info().await.unwrap();
122 let node1_id = node1_info.id;
123 let node1_enode = node1_info.enode;
124
125 let added = provider2.add_peer(&node1_enode).await.unwrap();
126 assert!(added);
127 geth2.wait_to_add_peer(&node1_id).unwrap();
128 let peers = provider2.peers().await.unwrap();
129 assert_eq!(peers[0].enode, node1_enode);
130 })
131 .await;
132 })
133 .await;
134 })
135 .await;
136 }
137}