Skip to main content

reth_network/test_utils/
testnet.rs

1//! A network implementation for testing purposes.
2
3use crate::{
4    builder::ETH_REQUEST_CHANNEL_CAPACITY,
5    config::rng_secret_key,
6    error::NetworkError,
7    eth_requests::EthRequestHandler,
8    protocol::IntoRlpxSubProtocol,
9    transactions::{
10        config::{StrictEthAnnouncementFilter, TransactionPropagationKind},
11        constants::tx_manager::DEFAULT_TX_MANAGER_CHANNEL_MEMORY_LIMIT_BYTES,
12        policy::NetworkPolicies,
13        TransactionsHandle, TransactionsManager, TransactionsManagerConfig,
14    },
15    NetworkConfigBuilder, NetworkHandle, NetworkManager, PeersConfig,
16};
17use alloy_eips::eip1559::ETHEREUM_BLOCK_GAS_LIMIT_30M;
18use futures::{FutureExt, StreamExt};
19use pin_project::pin_project;
20use reth_chainspec::{ChainSpecProvider, EthereumHardforks, Hardforks};
21use reth_eth_wire::{
22    protocol::Protocol, DisconnectReason, EthNetworkPrimitives, HelloMessageWithProtocols,
23};
24use reth_ethereum_primitives::{PooledTransactionVariant, TransactionSigned};
25use reth_evm_ethereum::EthEvmConfig;
26use reth_metrics::common::mpsc::memory_bounded_channel;
27use reth_network_api::{
28    events::{PeerEvent, SessionInfo},
29    test_utils::{PeersHandle, PeersHandleProvider},
30    NetworkEvent, NetworkEventListenerProvider, NetworkInfo, PeerRequest, Peers,
31};
32use reth_network_p2p::error::{RequestError, RequestResult};
33use reth_network_peers::PeerId;
34use reth_storage_api::{
35    noop::NoopProvider, BalProvider, BlockNumReader, BlockReader, BlockReaderIdExt, HeaderProvider,
36    StateProviderFactory, StateRangeProviderFactory,
37};
38use reth_tasks::Runtime;
39use reth_tokio_util::EventStream;
40use reth_transaction_pool::{
41    blobstore::InMemoryBlobStore, test_utils::TestPool, EthTransactionPool, PoolTransaction,
42    TransactionPool, TransactionValidationTaskExecutor,
43};
44use secp256k1::SecretKey;
45use std::{
46    fmt,
47    future::Future,
48    net::{Ipv4Addr, SocketAddr, SocketAddrV4},
49    pin::Pin,
50    task::{Context, Poll},
51};
52use tokio::{
53    sync::{mpsc::channel, oneshot},
54    task::JoinHandle,
55};
56
57/// A test network consisting of multiple peers.
58pub struct Testnet<C, Pool> {
59    /// All running peers in the network.
60    peers: Vec<Peer<C, Pool>>,
61}
62
63// === impl Testnet ===
64
65impl<C> Testnet<C, TestPool>
66where
67    C: BlockNumReader + ChainSpecProvider<ChainSpec: Hardforks> + Clone + 'static,
68{
69    /// Creates a new [`Testnet`] with a peer for each of the given [`PeerConfig`]s.
70    ///
71    /// The peers are launched concurrently.
72    ///
73    /// # Panics
74    ///
75    /// If a peer fails to launch.
76    pub async fn from_configs(configs: impl IntoIterator<Item = PeerConfig<C>>) -> Self {
77        let peers = futures::future::try_join_all(configs.into_iter().map(PeerConfig::launch))
78            .await
79            .expect("failed to launch testnet peers");
80        Self { peers }
81    }
82
83    /// Creates a new [`Testnet`] with the given number of peers that serve data from `provider`.
84    ///
85    /// # Panics
86    ///
87    /// If a peer fails to launch.
88    pub async fn create_with(num_peers: usize, provider: C) -> Self {
89        Self::from_configs((0..num_peers).map(|_| PeerConfig::new(provider.clone()))).await
90    }
91
92    /// Add a peer to the [`Testnet`] with the given [`PeerConfig`].
93    pub async fn add_peer_with_config(
94        &mut self,
95        config: PeerConfig<C>,
96    ) -> Result<(), NetworkError> {
97        self.peers.push(config.launch().await?);
98        Ok(())
99    }
100}
101
102impl Testnet<NoopProvider, TestPool> {
103    /// Creates a new [`Testnet`] with the given number of peers.
104    ///
105    /// # Panics
106    ///
107    /// If a peer fails to launch.
108    pub async fn create(num_peers: usize) -> Self {
109        Self::create_with(num_peers, NoopProvider::default()).await
110    }
111}
112
113impl<C, Pool> Testnet<C, Pool>
114where
115    C: BlockReader + Clone + 'static,
116    Pool: TransactionPool,
117{
118    /// Return a mutable slice of all peers.
119    pub fn peers_mut(&mut self) -> &mut [Peer<C, Pool>] {
120        &mut self.peers
121    }
122
123    /// Return a slice of all peers.
124    pub fn peers(&self) -> &[Peer<C, Pool>] {
125        &self.peers
126    }
127
128    /// Maps the pool of each peer with the given closure
129    pub fn map_pool<F, P>(self, f: F) -> Testnet<C, P>
130    where
131        F: Fn(Peer<C, Pool>) -> Peer<C, P>,
132        P: TransactionPool,
133    {
134        Testnet { peers: self.peers.into_iter().map(f).collect() }
135    }
136
137    /// Apply a closure on each peer
138    pub fn for_each_mut<F>(&mut self, f: F)
139    where
140        F: FnMut(&mut Peer<C, Pool>),
141    {
142        self.peers.iter_mut().for_each(f)
143    }
144
145    /// Installs an eth request handler on each peer.
146    pub fn with_request_handlers(mut self) -> Self
147    where
148        C: BalProvider,
149    {
150        self.for_each_mut(Peer::install_request_handler);
151        self
152    }
153}
154
155impl<C, Pool> Testnet<C, Pool>
156where
157    C: ChainSpecProvider<ChainSpec: EthereumHardforks>
158        + StateProviderFactory
159        + BlockReaderIdExt
160        + HeaderProvider<Header = alloy_consensus::Header>
161        + Clone
162        + 'static,
163    Pool: TransactionPool,
164{
165    /// Installs an eth pool on each peer
166    pub fn with_eth_pool(
167        self,
168    ) -> Testnet<C, EthTransactionPool<C, InMemoryBlobStore, EthEvmConfig>> {
169        self.with_eth_pool_config(Default::default())
170    }
171
172    /// Installs an eth pool on each peer with custom transaction manager config
173    pub fn with_eth_pool_config(
174        self,
175        tx_manager_config: TransactionsManagerConfig,
176    ) -> Testnet<C, EthTransactionPool<C, InMemoryBlobStore, EthEvmConfig>> {
177        self.with_eth_pool_config_and_policy(tx_manager_config, Default::default())
178    }
179
180    /// Installs an eth pool on each peer with custom transaction manager config and policy.
181    pub fn with_eth_pool_config_and_policy(
182        self,
183        tx_manager_config: TransactionsManagerConfig,
184        policy: TransactionPropagationKind,
185    ) -> Testnet<C, EthTransactionPool<C, InMemoryBlobStore, EthEvmConfig>> {
186        self.map_pool(|peer| {
187            let blob_store = InMemoryBlobStore::default();
188            // Test peers usually sit on mainnet's genesis, whose 5,000 gas limit would reject
189            // every transaction.
190            let validator = TransactionValidationTaskExecutor::eth_builder(
191                peer.client.clone(),
192                EthEvmConfig::mainnet(),
193            )
194            .set_block_gas_limit(ETHEREUM_BLOCK_GAS_LIMIT_30M)
195            .build_with_tasks(Runtime::test(), blob_store.clone());
196            let pool = EthTransactionPool::eth_pool(validator, blob_store, Default::default());
197            peer.map_transactions_manager(pool, tx_manager_config.clone(), policy)
198        })
199    }
200}
201
202impl<C, Pool> Testnet<C, Pool>
203where
204    C: TestnetProvider + Clone,
205    Pool: TestnetPool,
206{
207    /// Spawns the testnet to a separate task
208    pub fn spawn(self) -> TestnetHandle<C, Pool> {
209        let (terminate, rx) = oneshot::channel::<oneshot::Sender<Self>>();
210        let peers = self.peers.iter().map(Peer::peer_handle).collect();
211        let mut net = self;
212        let handle = tokio::task::spawn(async move {
213            let tx = tokio::select! {
214                _ = &mut net => None,
215                tx = rx => tx.ok(),
216            };
217            if let Some(tx) = tx {
218                let _ = tx.send(net);
219            }
220        });
221
222        TestnetHandle { _handle: handle, peers, terminate }
223    }
224}
225
226impl<C, Pool> fmt::Debug for Testnet<C, Pool> {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        f.debug_struct("Testnet").finish_non_exhaustive()
229    }
230}
231
232impl<C, Pool> Future for Testnet<C, Pool>
233where
234    C: TestnetProvider,
235    Pool: TestnetPool,
236{
237    type Output = ();
238
239    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
240        let this = self.get_mut();
241        for peer in &mut this.peers {
242            let _ = peer.poll_unpin(cx);
243        }
244        Poll::Pending
245    }
246}
247
248/// A handle to a [`Testnet`] that can be shared.
249#[derive(Debug)]
250pub struct TestnetHandle<C, Pool> {
251    _handle: JoinHandle<()>,
252    peers: Vec<PeerHandle<Pool>>,
253    terminate: oneshot::Sender<oneshot::Sender<Testnet<C, Pool>>>,
254}
255
256// === impl TestnetHandle ===
257
258impl<C, Pool> TestnetHandle<C, Pool> {
259    /// Terminates the task and returns the [`Testnet`] back.
260    pub async fn terminate(self) -> Testnet<C, Pool> {
261        let (tx, rx) = oneshot::channel();
262        self.terminate.send(tx).unwrap();
263        rx.await.unwrap()
264    }
265
266    /// Returns the [`PeerHandle`]s of this [`Testnet`].
267    pub fn peers(&self) -> &[PeerHandle<Pool>] {
268        &self.peers
269    }
270
271    /// Returns the [`PeerHandle`]s of this [`Testnet`] as an array, so they can be destructured:
272    /// `let [peer0, peer1] = net.peers_array();`.
273    ///
274    /// # Panics
275    ///
276    /// If the testnet does not have exactly `N` peers.
277    pub fn peers_array<const N: usize>(&self) -> &[PeerHandle<Pool>; N] {
278        self.peers.as_slice().try_into().unwrap_or_else(|_| {
279            panic!("expected {N} peers, but the testnet has {}", self.peers.len())
280        })
281    }
282
283    /// Connects all peers with each other.
284    ///
285    /// This establishes sessions concurrently between all peers.
286    ///
287    /// Returns once all sessions are established.
288    pub async fn connect_peers(&self) {
289        if self.peers.len() < 2 {
290            return
291        }
292
293        // add an event stream for _each_ peer
294        let streams = self.peers.iter().map(PeerHandle::event_stream).collect::<Vec<_>>();
295
296        // add all peers to each other
297        for (idx, handle) in self.peers.iter().enumerate().take(self.peers.len() - 1) {
298            for neighbour in &self.peers[idx + 1..] {
299                handle.add_peer(neighbour);
300            }
301        }
302
303        // await all sessions to be established
304        let num_sessions_per_peer = self.peers.len() - 1;
305        let fut = streams.into_iter().map(|mut stream| async move {
306            stream.take_session_established(num_sessions_per_peer).await
307        });
308
309        futures::future::join_all(fut).await;
310    }
311}
312
313/// A peer in the [`Testnet`].
314#[pin_project]
315#[derive(Debug)]
316pub struct Peer<C, Pool = TestPool> {
317    #[pin]
318    network: NetworkManager<EthNetworkPrimitives>,
319    #[pin]
320    request_handler: Option<EthRequestHandler<C, EthNetworkPrimitives>>,
321    #[pin]
322    transactions_manager: Option<TransactionsManager<Pool, EthNetworkPrimitives>>,
323    pool: Option<Pool>,
324    client: C,
325}
326
327// === impl Peer ===
328
329impl<C, Pool> Peer<C, Pool>
330where
331    C: BlockReader + Clone + 'static,
332    Pool: TransactionPool,
333{
334    /// Returns the number of connected peers.
335    pub fn num_peers(&self) -> usize {
336        self.network.num_connected_peers()
337    }
338
339    /// Adds an additional protocol handler to the peer.
340    pub fn add_rlpx_sub_protocol(&mut self, protocol: impl IntoRlpxSubProtocol) {
341        self.network.add_rlpx_sub_protocol(protocol);
342    }
343
344    /// Returns a handle to the peer's network.
345    pub fn peer_handle(&self) -> PeerHandle<Pool> {
346        PeerHandle {
347            network: self.network.handle().clone(),
348            pool: self.pool.clone(),
349            transactions: self.transactions_manager.as_ref().map(|mgr| mgr.handle()),
350        }
351    }
352
353    /// The address that listens for incoming connections.
354    pub const fn local_addr(&self) -> SocketAddr {
355        self.network.local_addr()
356    }
357
358    /// The [`PeerId`] of this peer.
359    pub fn peer_id(&self) -> PeerId {
360        *self.network.peer_id()
361    }
362
363    /// Returns mutable access to the network.
364    pub const fn network_mut(&mut self) -> &mut NetworkManager<EthNetworkPrimitives> {
365        &mut self.network
366    }
367
368    /// Returns the [`NetworkHandle`] of this peer.
369    pub fn handle(&self) -> NetworkHandle<EthNetworkPrimitives> {
370        self.network.handle().clone()
371    }
372
373    /// Returns the [`TestPool`] of this peer.
374    pub const fn pool(&self) -> Option<&Pool> {
375        self.pool.as_ref()
376    }
377
378    /// Set a new request handler that's connected to the peer's network
379    pub fn install_request_handler(&mut self)
380    where
381        C: BalProvider,
382    {
383        let (tx, rx) = channel(ETH_REQUEST_CHANNEL_CAPACITY);
384        self.network.set_eth_request_handler(tx);
385        let request_handler = EthRequestHandler::new(self.client.clone(), rx);
386        self.request_handler = Some(request_handler);
387    }
388
389    /// Set a new transactions manager with the default config that's connected to the peer's
390    /// network.
391    pub fn install_transactions_manager(&mut self, pool: Pool) {
392        self.transactions_manager = Some(new_transactions_manager(
393            &mut self.network,
394            pool.clone(),
395            Default::default(),
396            Default::default(),
397        ));
398        self.pool = Some(pool);
399    }
400
401    /// Replaces the pool with `pool` and connects a new transactions manager with the given config
402    /// and propagation policy to the peer's network.
403    pub fn map_transactions_manager<P>(
404        self,
405        pool: P,
406        config: TransactionsManagerConfig,
407        policy: TransactionPropagationKind,
408    ) -> Peer<C, P>
409    where
410        P: TransactionPool,
411    {
412        let Self { mut network, request_handler, client, .. } = self;
413        let transactions_manager =
414            new_transactions_manager(&mut network, pool.clone(), config, policy);
415        Peer {
416            network,
417            request_handler,
418            transactions_manager: Some(transactions_manager),
419            pool: Some(pool),
420            client,
421        }
422    }
423}
424
425impl<C, Pool> Future for Peer<C, Pool>
426where
427    C: TestnetProvider,
428    Pool: TestnetPool,
429{
430    type Output = ();
431
432    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
433        let this = self.project();
434
435        if let Some(request) = this.request_handler.as_pin_mut() {
436            let _ = request.poll(cx);
437        }
438
439        if let Some(tx_manager) = this.transactions_manager.as_pin_mut() {
440            let _ = tx_manager.poll(cx);
441        }
442
443        this.network.poll(cx)
444    }
445}
446
447/// A helper config for setting up the reth networking stack.
448///
449/// The network config is built when the peer is launched.
450#[derive(Debug)]
451pub struct PeerConfig<C = NoopProvider> {
452    client: C,
453    secret_key: SecretKey,
454    protocols: Option<Vec<Protocol>>,
455    peers_config: PeersConfig,
456}
457
458/// A handle to a peer in the [`Testnet`].
459#[derive(Debug)]
460pub struct PeerHandle<Pool> {
461    network: NetworkHandle<EthNetworkPrimitives>,
462    transactions: Option<TransactionsHandle<EthNetworkPrimitives>>,
463    pool: Option<Pool>,
464}
465
466// === impl PeerHandle ===
467
468impl<Pool> PeerHandle<Pool> {
469    /// Returns the [`PeerId`] used in the network.
470    pub fn peer_id(&self) -> &PeerId {
471        self.network.peer_id()
472    }
473
474    /// Returns the [`PeersHandle`] from the network.
475    pub fn peer_handle(&self) -> &PeersHandle {
476        self.network.peers_handle()
477    }
478
479    /// Returns the local socket as configured for the network.
480    pub fn local_addr(&self) -> SocketAddr {
481        self.network.local_addr()
482    }
483
484    /// Creates a new [`NetworkEvent`] listener channel.
485    pub fn event_listener(&self) -> EventStream<NetworkEvent> {
486        self.network.event_listener()
487    }
488
489    /// Returns the [`TransactionsHandle`] of this peer.
490    pub const fn transactions(&self) -> Option<&TransactionsHandle> {
491        self.transactions.as_ref()
492    }
493
494    /// Returns the [`TestPool`] of this peer.
495    pub const fn pool(&self) -> Option<&Pool> {
496        self.pool.as_ref()
497    }
498
499    /// Returns the [`NetworkHandle`] of this peer.
500    pub const fn network(&self) -> &NetworkHandle<EthNetworkPrimitives> {
501        &self.network
502    }
503
504    /// Adds `other` to the peer set, so this peer connects to it.
505    pub fn add_peer<P>(&self, other: &PeerHandle<P>) {
506        self.network.add_peer(*other.peer_id(), other.local_addr());
507    }
508
509    /// Adds `other` to the peer set as a trusted peer, so this peer connects to it.
510    pub fn add_trusted_peer<P>(&self, other: &PeerHandle<P>) {
511        self.network.add_trusted_peer(*other.peer_id(), other.local_addr());
512    }
513
514    /// Creates a new [`NetworkEventStream`] of this peer's network events.
515    pub fn event_stream(&self) -> NetworkEventStream {
516        NetworkEventStream::new(self.event_listener())
517    }
518
519    /// Sends the request created by `request` to `peer_id` and awaits the response.
520    ///
521    /// Returns [`RequestError::ChannelClosed`] if the response channel is dropped.
522    pub async fn request<R>(
523        &self,
524        peer_id: PeerId,
525        request: impl FnOnce(oneshot::Sender<RequestResult<R>>) -> PeerRequest,
526    ) -> RequestResult<R> {
527        let (tx, rx) = oneshot::channel();
528        self.network.send_request(peer_id, request(tx));
529        rx.await.unwrap_or(Err(RequestError::ChannelClosed))
530    }
531}
532
533// === impl PeerConfig ===
534
535impl<C> PeerConfig<C> {
536    /// Creates a config for a peer with a random secret key that serves data from `client`.
537    ///
538    /// The peer's devp2p and discovery bind to any available port.
539    pub fn new(client: C) -> Self {
540        Self {
541            client,
542            secret_key: rng_secret_key(),
543            protocols: None,
544            peers_config: PeersConfig::test(),
545        }
546    }
547
548    /// Sets the secret key, which determines the peer's [`PeerId`].
549    pub const fn with_secret_key(mut self, secret_key: SecretKey) -> Self {
550        self.secret_key = secret_key;
551        self
552    }
553
554    /// Sets the protocols advertised in the hello message, e.g. the supported eth versions.
555    pub fn with_protocols(mut self, protocols: impl IntoIterator<Item: Into<Protocol>>) -> Self {
556        self.protocols = Some(protocols.into_iter().map(Into::into).collect());
557        self
558    }
559
560    /// Sets the [`PeersConfig`], which defaults to [`PeersConfig::test`].
561    pub fn with_peers_config(mut self, peers_config: PeersConfig) -> Self {
562        self.peers_config = peers_config;
563        self
564    }
565
566    /// Launches the network and returns the [`Peer`] that manages it.
567    pub async fn launch(self) -> Result<Peer<C>, NetworkError>
568    where
569        C: BlockNumReader + ChainSpecProvider<ChainSpec: Hardforks> + Clone + 'static,
570    {
571        let Self { client, secret_key, protocols, peers_config } = self;
572        let mut builder = NetworkConfigBuilder::new(secret_key, Runtime::test())
573            .listener_addr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)))
574            .discovery_addr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)))
575            .disable_dns_discovery()
576            .disable_discv4_discovery()
577            .peer_config(peers_config);
578        if let Some(protocols) = protocols {
579            // `NetworkConfigBuilder::build` re-derives snap advertisement from `snap_enabled`,
580            // which would otherwise silently strip a manually included `snap` capability.
581            let snap_enabled = protocols.iter().any(|p| p.cap.name == Protocol::snap_2().cap.name);
582            let hello_message = HelloMessageWithProtocols::builder(builder.get_peer_id())
583                .protocols(protocols)
584                .build();
585            builder = builder.with_snap(snap_enabled).hello_message(hello_message);
586        }
587
588        let network = NetworkManager::new(builder.build(client.clone())).await?;
589        Ok(Peer { network, client, request_handler: None, transactions_manager: None, pool: None })
590    }
591}
592
593impl Default for PeerConfig {
594    fn default() -> Self {
595        Self::new(NoopProvider::default())
596    }
597}
598
599/// A helper type to await network events
600///
601/// This makes it easier to await established connections
602#[derive(Debug)]
603pub struct NetworkEventStream {
604    inner: EventStream<NetworkEvent>,
605}
606
607// === impl NetworkEventStream ===
608
609impl NetworkEventStream {
610    /// Create a new [`NetworkEventStream`] from the given network event receiver stream.
611    pub const fn new(inner: EventStream<NetworkEvent>) -> Self {
612        Self { inner }
613    }
614
615    /// Awaits the next event for a session to be closed
616    pub async fn next_session_closed(&mut self) -> Option<(PeerId, Option<DisconnectReason>)> {
617        while let Some(ev) = self.inner.next().await {
618            if let NetworkEvent::Peer(PeerEvent::SessionClosed { peer_id, reason }) = ev {
619                return Some((peer_id, reason))
620            }
621        }
622        None
623    }
624
625    /// Awaits the next event for an established session
626    pub async fn next_session_established(&mut self) -> Option<PeerId> {
627        while let Some(ev) = self.inner.next().await {
628            match ev {
629                NetworkEvent::ActivePeerSession { info, .. } |
630                NetworkEvent::Peer(PeerEvent::SessionEstablished(info)) => {
631                    return Some(info.peer_id)
632                }
633                _ => {}
634            }
635        }
636        None
637    }
638
639    /// Awaits the next `num` events for an established session
640    pub async fn take_session_established(&mut self, mut num: usize) -> Vec<PeerId> {
641        if num == 0 {
642            return Vec::new();
643        }
644        let mut peers = Vec::with_capacity(num);
645        while let Some(ev) = self.inner.next().await {
646            if let NetworkEvent::ActivePeerSession { info: SessionInfo { peer_id, .. }, .. } = ev {
647                peers.push(peer_id);
648                num -= 1;
649                if num == 0 {
650                    return peers;
651                }
652            }
653        }
654        peers
655    }
656
657    /// Returns the peer of the next event if it is a [`PeerEvent::PeerAdded`].
658    pub async fn peer_added(&mut self) -> Option<PeerId> {
659        match self.inner.next().await {
660            Some(NetworkEvent::Peer(PeerEvent::PeerAdded(peer_id))) => Some(peer_id),
661            _ => None,
662        }
663    }
664
665    /// Returns the peer of the next event if it is a [`PeerEvent::PeerRemoved`].
666    pub async fn peer_removed(&mut self) -> Option<PeerId> {
667        match self.inner.next().await {
668            Some(NetworkEvent::Peer(PeerEvent::PeerRemoved(peer_id))) => Some(peer_id),
669            _ => None,
670        }
671    }
672}
673
674/// A provider that can serve all eth requests of a [`Peer`] in a spawned [`Testnet`].
675pub trait TestnetProvider:
676    BlockReader<
677        Block = reth_ethereum_primitives::Block,
678        Receipt = reth_ethereum_primitives::Receipt,
679        Header = alloy_consensus::Header,
680    > + BalProvider
681    + StateProviderFactory
682    + StateRangeProviderFactory
683    + Clone
684    + Unpin
685    + 'static
686{
687}
688
689impl<T> TestnetProvider for T where
690    T: BlockReader<
691            Block = reth_ethereum_primitives::Block,
692            Receipt = reth_ethereum_primitives::Receipt,
693            Header = alloy_consensus::Header,
694        > + BalProvider
695        + StateProviderFactory
696        + StateRangeProviderFactory
697        + Clone
698        + Unpin
699        + 'static
700{
701}
702
703/// A transaction pool that can back the transactions manager of a [`Peer`] in a spawned
704/// [`Testnet`].
705pub trait TestnetPool:
706    TransactionPool<
707        Transaction: PoolTransaction<
708            Consensus = TransactionSigned,
709            Pooled = PooledTransactionVariant,
710        >,
711    > + Unpin
712    + 'static
713{
714}
715
716impl<T> TestnetPool for T where
717    T: TransactionPool<
718            Transaction: PoolTransaction<
719                Consensus = TransactionSigned,
720                Pooled = PooledTransactionVariant,
721            >,
722        > + Unpin
723        + 'static
724{
725}
726
727/// Creates a transactions manager for `pool` that is connected to `network`.
728fn new_transactions_manager<Pool: TransactionPool>(
729    network: &mut NetworkManager<EthNetworkPrimitives>,
730    pool: Pool,
731    config: TransactionsManagerConfig,
732    policy: TransactionPropagationKind,
733) -> TransactionsManager<Pool, EthNetworkPrimitives> {
734    let (tx, rx) =
735        memory_bounded_channel(DEFAULT_TX_MANAGER_CHANNEL_MEMORY_LIMIT_BYTES, "test_tx_channel");
736    network.set_transactions(tx);
737    let policies = NetworkPolicies::new(policy, StrictEthAnnouncementFilter::default());
738    TransactionsManager::with_policy(network.handle().clone(), pool, rx, config, policies)
739}