1use crate::{
4 builder::ETH_REQUEST_CHANNEL_CAPACITY,
5 error::NetworkError,
6 eth_requests::EthRequestHandler,
7 protocol::IntoRlpxSubProtocol,
8 transactions::{
9 config::{StrictEthAnnouncementFilter, TransactionPropagationKind},
10 policy::NetworkPolicies,
11 TransactionsHandle, TransactionsManager, TransactionsManagerConfig,
12 },
13 NetworkConfig, NetworkConfigBuilder, NetworkHandle, NetworkManager, PeersConfig,
14};
15use futures::{FutureExt, StreamExt};
16use pin_project::pin_project;
17use reth_chainspec::{ChainSpecProvider, EthereumHardforks, Hardforks};
18use reth_eth_wire::{
19 protocol::Protocol, DisconnectReason, EthNetworkPrimitives, HelloMessageWithProtocols,
20};
21use reth_ethereum_primitives::{PooledTransactionVariant, TransactionSigned};
22use reth_evm_ethereum::EthEvmConfig;
23use reth_metrics::common::mpsc::memory_bounded_channel;
24use reth_network_api::{
25 events::{PeerEvent, SessionInfo},
26 test_utils::{PeersHandle, PeersHandleProvider},
27 NetworkEvent, NetworkEventListenerProvider, NetworkInfo, Peers,
28};
29use reth_network_peers::PeerId;
30use reth_storage_api::{
31 noop::NoopProvider, BalProvider, BlockReader, BlockReaderIdExt, HeaderProvider,
32 StateProviderFactory, StateRangeProviderFactory,
33};
34use reth_tasks::Runtime;
35use reth_tokio_util::EventStream;
36use reth_transaction_pool::{
37 blobstore::InMemoryBlobStore,
38 test_utils::{TestPool, TestPoolBuilder},
39 EthTransactionPool, PoolTransaction, TransactionPool, TransactionValidationTaskExecutor,
40};
41use secp256k1::SecretKey;
42use std::{
43 fmt,
44 future::Future,
45 net::{Ipv4Addr, SocketAddr, SocketAddrV4},
46 pin::Pin,
47 task::{Context, Poll},
48};
49use tokio::{
50 sync::{mpsc::channel, oneshot},
51 task::JoinHandle,
52};
53
54use crate::transactions::constants::tx_manager::DEFAULT_TX_MANAGER_CHANNEL_MEMORY_LIMIT_BYTES;
55
56pub struct Testnet<C, Pool> {
58 peers: Vec<Peer<C, Pool>>,
60}
61
62impl<C> Testnet<C, TestPool>
65where
66 C: BlockReader + HeaderProvider + Clone + 'static + ChainSpecProvider<ChainSpec: Hardforks>,
67{
68 pub async fn create_with(num_peers: usize, provider: C) -> Self {
70 Self::try_create_with(num_peers, provider).await.unwrap()
71 }
72
73 pub async fn try_create_with(num_peers: usize, provider: C) -> Result<Self, NetworkError> {
75 let mut this = Self { peers: Vec::with_capacity(num_peers) };
76 for _ in 0..num_peers {
77 let config = PeerConfig::new(provider.clone());
78 this.add_peer_with_config(config).await?;
79 }
80 Ok(this)
81 }
82
83 pub async fn extend_peer_with_config(
86 &mut self,
87 configs: impl IntoIterator<Item = PeerConfig<C>>,
88 ) -> Result<(), NetworkError> {
89 let peers = configs.into_iter().map(|c| c.launch()).collect::<Vec<_>>();
90 let peers = futures::future::join_all(peers).await;
91 for peer in peers {
92 self.peers.push(peer?);
93 }
94 Ok(())
95 }
96}
97
98impl<C, Pool> Testnet<C, Pool>
99where
100 C: BlockReader + HeaderProvider + Clone + 'static,
101 Pool: TransactionPool,
102{
103 pub fn peers_mut(&mut self) -> &mut [Peer<C, Pool>] {
105 &mut self.peers
106 }
107
108 pub fn peers(&self) -> &[Peer<C, Pool>] {
110 &self.peers
111 }
112
113 pub fn remove_peer(&mut self, index: usize) -> Peer<C, Pool> {
118 self.peers.remove(index)
119 }
120
121 pub fn peers_iter_mut(&mut self) -> impl Iterator<Item = &mut Peer<C, Pool>> + '_ {
123 self.peers.iter_mut()
124 }
125
126 pub fn peers_iter(&self) -> impl Iterator<Item = &Peer<C, Pool>> + '_ {
128 self.peers.iter()
129 }
130
131 pub async fn add_peer_with_config(
133 &mut self,
134 config: PeerConfig<C>,
135 ) -> Result<(), NetworkError> {
136 let PeerConfig { config, client, secret_key } = config;
137
138 let network = NetworkManager::new(config).await?;
139 let peer = Peer {
140 network,
141 client,
142 secret_key,
143 request_handler: None,
144 transactions_manager: None,
145 pool: None,
146 };
147 self.peers.push(peer);
148 Ok(())
149 }
150
151 pub fn handles(&self) -> impl Iterator<Item = NetworkHandle<EthNetworkPrimitives>> + '_ {
153 self.peers.iter().map(|p| p.handle())
154 }
155
156 pub fn map_pool<F, P>(self, f: F) -> Testnet<C, P>
158 where
159 F: Fn(Peer<C, Pool>) -> Peer<C, P>,
160 P: TransactionPool,
161 {
162 Testnet { peers: self.peers.into_iter().map(f).collect() }
163 }
164
165 pub fn for_each<F>(&self, f: F)
167 where
168 F: Fn(&Peer<C, Pool>),
169 {
170 self.peers.iter().for_each(f)
171 }
172
173 pub fn for_each_mut<F>(&mut self, f: F)
175 where
176 F: FnMut(&mut Peer<C, Pool>),
177 {
178 self.peers.iter_mut().for_each(f)
179 }
180}
181
182impl<C, Pool> Testnet<C, Pool>
183where
184 C: ChainSpecProvider<ChainSpec: EthereumHardforks>
185 + StateProviderFactory
186 + BlockReaderIdExt
187 + HeaderProvider<Header = alloy_consensus::Header>
188 + Clone
189 + 'static,
190 Pool: TransactionPool,
191{
192 pub fn with_eth_pool(
194 self,
195 ) -> Testnet<C, EthTransactionPool<C, InMemoryBlobStore, EthEvmConfig>> {
196 self.map_pool(|peer| {
197 let blob_store = InMemoryBlobStore::default();
198 let pool = TransactionValidationTaskExecutor::eth(
199 peer.client.clone(),
200 EthEvmConfig::mainnet(),
201 blob_store.clone(),
202 Runtime::test(),
203 );
204 peer.map_transactions_manager(EthTransactionPool::eth_pool(
205 pool,
206 blob_store,
207 Default::default(),
208 ))
209 })
210 }
211
212 pub fn with_eth_pool_config(
214 self,
215 tx_manager_config: TransactionsManagerConfig,
216 ) -> Testnet<C, EthTransactionPool<C, InMemoryBlobStore, EthEvmConfig>> {
217 self.with_eth_pool_config_and_policy(tx_manager_config, Default::default())
218 }
219
220 pub fn with_eth_pool_config_and_policy(
222 self,
223 tx_manager_config: TransactionsManagerConfig,
224 policy: TransactionPropagationKind,
225 ) -> Testnet<C, EthTransactionPool<C, InMemoryBlobStore, EthEvmConfig>> {
226 self.map_pool(|peer| {
227 let blob_store = InMemoryBlobStore::default();
228 let pool = TransactionValidationTaskExecutor::eth(
229 peer.client.clone(),
230 EthEvmConfig::mainnet(),
231 blob_store.clone(),
232 Runtime::test(),
233 );
234
235 peer.map_transactions_manager_with(
236 EthTransactionPool::eth_pool(pool, blob_store, Default::default()),
237 tx_manager_config.clone(),
238 policy,
239 )
240 })
241 }
242}
243
244impl<C, Pool> Testnet<C, Pool>
245where
246 C: BlockReader<
247 Block = reth_ethereum_primitives::Block,
248 Receipt = reth_ethereum_primitives::Receipt,
249 Header = alloy_consensus::Header,
250 > + HeaderProvider
251 + BalProvider
252 + StateProviderFactory
253 + StateRangeProviderFactory
254 + Clone
255 + Unpin
256 + 'static,
257 Pool: TransactionPool<
258 Transaction: PoolTransaction<
259 Consensus = TransactionSigned,
260 Pooled = PooledTransactionVariant,
261 >,
262 > + Unpin
263 + 'static,
264{
265 pub fn spawn(self) -> TestnetHandle<C, Pool> {
267 let (tx, rx) = oneshot::channel::<oneshot::Sender<Self>>();
268 let peers = self.peers.iter().map(|peer| peer.peer_handle()).collect::<Vec<_>>();
269 let mut net = self;
270 let handle = tokio::task::spawn(async move {
271 let mut tx = None;
272 tokio::select! {
273 _ = &mut net => {}
274 inc = rx => {
275 tx = inc.ok();
276 }
277 }
278 if let Some(tx) = tx {
279 let _ = tx.send(net);
280 }
281 });
282
283 TestnetHandle { _handle: handle, peers, terminate: tx }
284 }
285}
286
287impl Testnet<NoopProvider, TestPool> {
288 pub async fn create(num_peers: usize) -> Self {
290 Self::try_create(num_peers).await.unwrap()
291 }
292
293 pub async fn try_create(num_peers: usize) -> Result<Self, NetworkError> {
295 let mut this = Self::default();
296
297 this.extend_peer_with_config((0..num_peers).map(|_| Default::default())).await?;
298 Ok(this)
299 }
300
301 pub async fn add_peer(&mut self) -> Result<(), NetworkError> {
303 self.add_peer_with_config(Default::default()).await
304 }
305}
306
307impl<C, Pool> Default for Testnet<C, Pool> {
308 fn default() -> Self {
309 Self { peers: Vec::new() }
310 }
311}
312
313impl<C, Pool> fmt::Debug for Testnet<C, Pool> {
314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315 f.debug_struct("Testnet {{}}").finish_non_exhaustive()
316 }
317}
318
319impl<C, Pool> Future for Testnet<C, Pool>
320where
321 C: BlockReader<
322 Block = reth_ethereum_primitives::Block,
323 Receipt = reth_ethereum_primitives::Receipt,
324 Header = alloy_consensus::Header,
325 > + HeaderProvider
326 + BalProvider
327 + StateProviderFactory
328 + StateRangeProviderFactory
329 + Unpin
330 + 'static,
331 Pool: TransactionPool<
332 Transaction: PoolTransaction<
333 Consensus = TransactionSigned,
334 Pooled = PooledTransactionVariant,
335 >,
336 > + Unpin
337 + 'static,
338{
339 type Output = ();
340
341 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
342 let this = self.get_mut();
343 for peer in &mut this.peers {
344 let _ = peer.poll_unpin(cx);
345 }
346 Poll::Pending
347 }
348}
349
350#[derive(Debug)]
352pub struct TestnetHandle<C, Pool> {
353 _handle: JoinHandle<()>,
354 peers: Vec<PeerHandle<Pool>>,
355 terminate: oneshot::Sender<oneshot::Sender<Testnet<C, Pool>>>,
356}
357
358impl<C, Pool> TestnetHandle<C, Pool> {
361 pub async fn terminate(self) -> Testnet<C, Pool> {
363 let (tx, rx) = oneshot::channel();
364 self.terminate.send(tx).unwrap();
365 rx.await.unwrap()
366 }
367
368 pub fn peers(&self) -> &[PeerHandle<Pool>] {
370 &self.peers
371 }
372
373 pub async fn connect_peers(&self) {
379 if self.peers.len() < 2 {
380 return
381 }
382
383 let streams =
385 self.peers.iter().map(|handle| NetworkEventStream::new(handle.event_listener()));
386
387 for (idx, handle) in self.peers.iter().enumerate().take(self.peers.len() - 1) {
389 for idx in (idx + 1)..self.peers.len() {
390 let neighbour = &self.peers[idx];
391 handle.network.add_peer(*neighbour.peer_id(), neighbour.local_addr());
392 }
393 }
394
395 let num_sessions_per_peer = self.peers.len() - 1;
397 let fut = streams.into_iter().map(|mut stream| async move {
398 stream.take_session_established(num_sessions_per_peer).await
399 });
400
401 futures::future::join_all(fut).await;
402 }
403}
404
405#[pin_project]
407#[derive(Debug)]
408pub struct Peer<C, Pool = TestPool> {
409 #[pin]
410 network: NetworkManager<EthNetworkPrimitives>,
411 #[pin]
412 request_handler: Option<EthRequestHandler<C, EthNetworkPrimitives>>,
413 #[pin]
414 transactions_manager: Option<TransactionsManager<Pool, EthNetworkPrimitives>>,
415 pool: Option<Pool>,
416 client: C,
417 secret_key: SecretKey,
418}
419
420impl<C, Pool> Peer<C, Pool>
423where
424 C: BlockReader + HeaderProvider + Clone + 'static,
425 Pool: TransactionPool,
426{
427 pub fn num_peers(&self) -> usize {
429 self.network.num_connected_peers()
430 }
431
432 pub fn add_rlpx_sub_protocol(&mut self, protocol: impl IntoRlpxSubProtocol) {
434 self.network.add_rlpx_sub_protocol(protocol);
435 }
436
437 pub fn peer_handle(&self) -> PeerHandle<Pool> {
439 PeerHandle {
440 network: self.network.handle().clone(),
441 pool: self.pool.clone(),
442 transactions: self.transactions_manager.as_ref().map(|mgr| mgr.handle()),
443 }
444 }
445
446 pub const fn local_addr(&self) -> SocketAddr {
448 self.network.local_addr()
449 }
450
451 pub fn peer_id(&self) -> PeerId {
453 *self.network.peer_id()
454 }
455
456 pub const fn network_mut(&mut self) -> &mut NetworkManager<EthNetworkPrimitives> {
458 &mut self.network
459 }
460
461 pub fn handle(&self) -> NetworkHandle<EthNetworkPrimitives> {
463 self.network.handle().clone()
464 }
465
466 pub const fn pool(&self) -> Option<&Pool> {
468 self.pool.as_ref()
469 }
470
471 pub fn install_request_handler(&mut self)
473 where
474 C: BalProvider,
475 {
476 let (tx, rx) = channel(ETH_REQUEST_CHANNEL_CAPACITY);
477 self.network.set_eth_request_handler(tx);
478 let peers = self.network.peers_handle();
479 let request_handler = EthRequestHandler::new(self.client.clone(), peers, rx);
480 self.request_handler = Some(request_handler);
481 }
482
483 pub fn install_transactions_manager(&mut self, pool: Pool) {
485 let (tx, rx) = memory_bounded_channel(
486 DEFAULT_TX_MANAGER_CHANNEL_MEMORY_LIMIT_BYTES,
487 "test_tx_channel",
488 );
489 self.network.set_transactions(tx);
490 let transactions_manager = TransactionsManager::new(
491 self.handle(),
492 pool.clone(),
493 rx,
494 TransactionsManagerConfig::default(),
495 );
496 self.transactions_manager = Some(transactions_manager);
497 self.pool = Some(pool);
498 }
499
500 pub fn map_transactions_manager<P>(self, pool: P) -> Peer<C, P>
502 where
503 P: TransactionPool,
504 {
505 let Self { mut network, request_handler, client, secret_key, .. } = self;
506 let (tx, rx) = memory_bounded_channel(
507 DEFAULT_TX_MANAGER_CHANNEL_MEMORY_LIMIT_BYTES,
508 "test_tx_channel",
509 );
510 network.set_transactions(tx);
511 let transactions_manager = TransactionsManager::new(
512 network.handle().clone(),
513 pool.clone(),
514 rx,
515 TransactionsManagerConfig::default(),
516 );
517 Peer {
518 network,
519 request_handler,
520 transactions_manager: Some(transactions_manager),
521 pool: Some(pool),
522 client,
523 secret_key,
524 }
525 }
526
527 pub fn map_transactions_manager_with_config<P>(
529 self,
530 pool: P,
531 config: TransactionsManagerConfig,
532 ) -> Peer<C, P>
533 where
534 P: TransactionPool,
535 {
536 self.map_transactions_manager_with(pool, config, Default::default())
537 }
538
539 pub fn map_transactions_manager_with<P>(
541 self,
542 pool: P,
543 config: TransactionsManagerConfig,
544 policy: TransactionPropagationKind,
545 ) -> Peer<C, P>
546 where
547 P: TransactionPool,
548 {
549 let Self { mut network, request_handler, client, secret_key, .. } = self;
550 let (tx, rx) = memory_bounded_channel(
551 DEFAULT_TX_MANAGER_CHANNEL_MEMORY_LIMIT_BYTES,
552 "test_tx_channel",
553 );
554 network.set_transactions(tx);
555
556 let announcement_policy = StrictEthAnnouncementFilter::default();
557 let policies = NetworkPolicies::new(policy, announcement_policy);
558
559 let transactions_manager = TransactionsManager::with_policy(
560 network.handle().clone(),
561 pool.clone(),
562 rx,
563 config,
564 policies,
565 );
566
567 Peer {
568 network,
569 request_handler,
570 transactions_manager: Some(transactions_manager),
571 pool: Some(pool),
572 client,
573 secret_key,
574 }
575 }
576}
577
578impl<C> Peer<C>
579where
580 C: BlockReader + HeaderProvider + Clone + 'static,
581{
582 pub fn install_test_pool(&mut self) {
584 self.install_transactions_manager(TestPoolBuilder::default().into())
585 }
586}
587
588impl<C, Pool> Future for Peer<C, Pool>
589where
590 C: BlockReader<
591 Block = reth_ethereum_primitives::Block,
592 Receipt = reth_ethereum_primitives::Receipt,
593 Header = alloy_consensus::Header,
594 > + HeaderProvider
595 + BalProvider
596 + StateProviderFactory
597 + StateRangeProviderFactory
598 + Unpin
599 + 'static,
600 Pool: TransactionPool<
601 Transaction: PoolTransaction<
602 Consensus = TransactionSigned,
603 Pooled = PooledTransactionVariant,
604 >,
605 > + Unpin
606 + 'static,
607{
608 type Output = ();
609
610 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
611 let this = self.project();
612
613 if let Some(request) = this.request_handler.as_pin_mut() {
614 let _ = request.poll(cx);
615 }
616
617 if let Some(tx_manager) = this.transactions_manager.as_pin_mut() {
618 let _ = tx_manager.poll(cx);
619 }
620
621 this.network.poll(cx)
622 }
623}
624
625#[derive(Debug)]
627pub struct PeerConfig<C = NoopProvider> {
628 config: NetworkConfig<C>,
629 client: C,
630 secret_key: SecretKey,
631}
632
633#[derive(Debug)]
635pub struct PeerHandle<Pool> {
636 network: NetworkHandle<EthNetworkPrimitives>,
637 transactions: Option<TransactionsHandle<EthNetworkPrimitives>>,
638 pool: Option<Pool>,
639}
640
641impl<Pool> PeerHandle<Pool> {
644 pub fn peer_id(&self) -> &PeerId {
646 self.network.peer_id()
647 }
648
649 pub fn peer_handle(&self) -> &PeersHandle {
651 self.network.peers_handle()
652 }
653
654 pub fn local_addr(&self) -> SocketAddr {
656 self.network.local_addr()
657 }
658
659 pub fn event_listener(&self) -> EventStream<NetworkEvent> {
661 self.network.event_listener()
662 }
663
664 pub const fn transactions(&self) -> Option<&TransactionsHandle> {
666 self.transactions.as_ref()
667 }
668
669 pub const fn pool(&self) -> Option<&Pool> {
671 self.pool.as_ref()
672 }
673
674 pub const fn network(&self) -> &NetworkHandle<EthNetworkPrimitives> {
676 &self.network
677 }
678}
679
680impl<C> PeerConfig<C>
683where
684 C: BlockReader + HeaderProvider + Clone + 'static,
685{
686 pub async fn launch(self) -> Result<Peer<C>, NetworkError> {
688 let Self { config, client, secret_key } = self;
689 let network = NetworkManager::new(config).await?;
690 let peer = Peer {
691 network,
692 client,
693 secret_key,
694 request_handler: None,
695 transactions_manager: None,
696 pool: None,
697 };
698 Ok(peer)
699 }
700
701 pub fn new(client: C) -> Self
704 where
705 C: ChainSpecProvider<ChainSpec: Hardforks>,
706 {
707 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
708 let config = Self::network_config_builder(secret_key).build(client.clone());
709 Self { config, client, secret_key }
710 }
711
712 pub fn with_secret_key(client: C, secret_key: SecretKey) -> Self
715 where
716 C: ChainSpecProvider<ChainSpec: Hardforks>,
717 {
718 let config = Self::network_config_builder(secret_key).build(client.clone());
719 Self { config, client, secret_key }
720 }
721
722 pub fn with_protocols(client: C, protocols: impl IntoIterator<Item = Protocol>) -> Self
724 where
725 C: ChainSpecProvider<ChainSpec: Hardforks>,
726 {
727 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
728 let protocols: Vec<Protocol> = protocols.into_iter().collect();
729 let snap_enabled = protocols.iter().any(|p| p.cap.name == Protocol::snap_2().cap.name);
732
733 let builder = Self::network_config_builder(secret_key).with_snap(snap_enabled);
734 let hello_message =
735 HelloMessageWithProtocols::builder(builder.get_peer_id()).protocols(protocols).build();
736 let config = builder.hello_message(hello_message).build(client.clone());
737
738 Self { config, client, secret_key }
739 }
740
741 fn network_config_builder(secret_key: SecretKey) -> NetworkConfigBuilder {
742 NetworkConfigBuilder::new(secret_key, Runtime::test())
743 .listener_addr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)))
744 .discovery_addr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)))
745 .disable_dns_discovery()
746 .disable_discv4_discovery()
747 .peer_config(PeersConfig::test())
748 }
749}
750
751impl Default for PeerConfig {
752 fn default() -> Self {
753 Self::new(NoopProvider::default())
754 }
755}
756
757#[derive(Debug)]
761pub struct NetworkEventStream {
762 inner: EventStream<NetworkEvent>,
763}
764
765impl NetworkEventStream {
768 pub const fn new(inner: EventStream<NetworkEvent>) -> Self {
770 Self { inner }
771 }
772
773 pub async fn next_session_closed(&mut self) -> Option<(PeerId, Option<DisconnectReason>)> {
775 while let Some(ev) = self.inner.next().await {
776 if let NetworkEvent::Peer(PeerEvent::SessionClosed { peer_id, reason }) = ev {
777 return Some((peer_id, reason))
778 }
779 }
780 None
781 }
782
783 pub async fn next_session_established(&mut self) -> Option<PeerId> {
785 while let Some(ev) = self.inner.next().await {
786 match ev {
787 NetworkEvent::ActivePeerSession { info, .. } |
788 NetworkEvent::Peer(PeerEvent::SessionEstablished(info)) => {
789 return Some(info.peer_id)
790 }
791 _ => {}
792 }
793 }
794 None
795 }
796
797 pub async fn take_session_established(&mut self, mut num: usize) -> Vec<PeerId> {
799 if num == 0 {
800 return Vec::new();
801 }
802 let mut peers = Vec::with_capacity(num);
803 while let Some(ev) = self.inner.next().await {
804 if let NetworkEvent::ActivePeerSession { info: SessionInfo { peer_id, .. }, .. } = ev {
805 peers.push(peer_id);
806 num -= 1;
807 if num == 0 {
808 return peers;
809 }
810 }
811 }
812 peers
813 }
814
815 pub async fn peer_added_and_established(&mut self) -> Option<PeerId> {
819 let peer_id = match self.inner.next().await {
820 Some(NetworkEvent::Peer(PeerEvent::PeerAdded(peer_id))) => peer_id,
821 _ => return None,
822 };
823
824 match self.inner.next().await {
825 Some(NetworkEvent::ActivePeerSession {
826 info: SessionInfo { peer_id: peer_id2, .. },
827 ..
828 }) => {
829 debug_assert_eq!(
830 peer_id, peer_id2,
831 "PeerAdded peer_id {peer_id} does not match SessionEstablished peer_id {peer_id2}"
832 );
833 Some(peer_id)
834 }
835 _ => None,
836 }
837 }
838
839 pub async fn peer_added(&mut self) -> Option<PeerId> {
841 let peer_id = match self.inner.next().await {
842 Some(NetworkEvent::Peer(PeerEvent::PeerAdded(peer_id))) => peer_id,
843 _ => return None,
844 };
845
846 Some(peer_id)
847 }
848
849 pub async fn peer_removed(&mut self) -> Option<PeerId> {
851 let peer_id = match self.inner.next().await {
852 Some(NetworkEvent::Peer(PeerEvent::PeerRemoved(peer_id))) => peer_id,
853 _ => return None,
854 };
855
856 Some(peer_id)
857 }
858}