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 peers = self.network.peers_handle();
386        let request_handler = EthRequestHandler::new(self.client.clone(), peers, rx);
387        self.request_handler = Some(request_handler);
388    }
389
390    /// Set a new transactions manager with the default config that's connected to the peer's
391    /// network.
392    pub fn install_transactions_manager(&mut self, pool: Pool) {
393        self.transactions_manager = Some(new_transactions_manager(
394            &mut self.network,
395            pool.clone(),
396            Default::default(),
397            Default::default(),
398        ));
399        self.pool = Some(pool);
400    }
401
402    /// Replaces the pool with `pool` and connects a new transactions manager with the given config
403    /// and propagation policy to the peer's network.
404    pub fn map_transactions_manager<P>(
405        self,
406        pool: P,
407        config: TransactionsManagerConfig,
408        policy: TransactionPropagationKind,
409    ) -> Peer<C, P>
410    where
411        P: TransactionPool,
412    {
413        let Self { mut network, request_handler, client, .. } = self;
414        let transactions_manager =
415            new_transactions_manager(&mut network, pool.clone(), config, policy);
416        Peer {
417            network,
418            request_handler,
419            transactions_manager: Some(transactions_manager),
420            pool: Some(pool),
421            client,
422        }
423    }
424}
425
426impl<C, Pool> Future for Peer<C, Pool>
427where
428    C: TestnetProvider,
429    Pool: TestnetPool,
430{
431    type Output = ();
432
433    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
434        let this = self.project();
435
436        if let Some(request) = this.request_handler.as_pin_mut() {
437            let _ = request.poll(cx);
438        }
439
440        if let Some(tx_manager) = this.transactions_manager.as_pin_mut() {
441            let _ = tx_manager.poll(cx);
442        }
443
444        this.network.poll(cx)
445    }
446}
447
448/// A helper config for setting up the reth networking stack.
449///
450/// The network config is built when the peer is launched.
451#[derive(Debug)]
452pub struct PeerConfig<C = NoopProvider> {
453    client: C,
454    secret_key: SecretKey,
455    protocols: Option<Vec<Protocol>>,
456    peers_config: PeersConfig,
457}
458
459/// A handle to a peer in the [`Testnet`].
460#[derive(Debug)]
461pub struct PeerHandle<Pool> {
462    network: NetworkHandle<EthNetworkPrimitives>,
463    transactions: Option<TransactionsHandle<EthNetworkPrimitives>>,
464    pool: Option<Pool>,
465}
466
467// === impl PeerHandle ===
468
469impl<Pool> PeerHandle<Pool> {
470    /// Returns the [`PeerId`] used in the network.
471    pub fn peer_id(&self) -> &PeerId {
472        self.network.peer_id()
473    }
474
475    /// Returns the [`PeersHandle`] from the network.
476    pub fn peer_handle(&self) -> &PeersHandle {
477        self.network.peers_handle()
478    }
479
480    /// Returns the local socket as configured for the network.
481    pub fn local_addr(&self) -> SocketAddr {
482        self.network.local_addr()
483    }
484
485    /// Creates a new [`NetworkEvent`] listener channel.
486    pub fn event_listener(&self) -> EventStream<NetworkEvent> {
487        self.network.event_listener()
488    }
489
490    /// Returns the [`TransactionsHandle`] of this peer.
491    pub const fn transactions(&self) -> Option<&TransactionsHandle> {
492        self.transactions.as_ref()
493    }
494
495    /// Returns the [`TestPool`] of this peer.
496    pub const fn pool(&self) -> Option<&Pool> {
497        self.pool.as_ref()
498    }
499
500    /// Returns the [`NetworkHandle`] of this peer.
501    pub const fn network(&self) -> &NetworkHandle<EthNetworkPrimitives> {
502        &self.network
503    }
504
505    /// Adds `other` to the peer set, so this peer connects to it.
506    pub fn add_peer<P>(&self, other: &PeerHandle<P>) {
507        self.network.add_peer(*other.peer_id(), other.local_addr());
508    }
509
510    /// Adds `other` to the peer set as a trusted peer, so this peer connects to it.
511    pub fn add_trusted_peer<P>(&self, other: &PeerHandle<P>) {
512        self.network.add_trusted_peer(*other.peer_id(), other.local_addr());
513    }
514
515    /// Creates a new [`NetworkEventStream`] of this peer's network events.
516    pub fn event_stream(&self) -> NetworkEventStream {
517        NetworkEventStream::new(self.event_listener())
518    }
519
520    /// Sends the request created by `request` to `peer_id` and awaits the response.
521    ///
522    /// Returns [`RequestError::ChannelClosed`] if the response channel is dropped.
523    pub async fn request<R>(
524        &self,
525        peer_id: PeerId,
526        request: impl FnOnce(oneshot::Sender<RequestResult<R>>) -> PeerRequest,
527    ) -> RequestResult<R> {
528        let (tx, rx) = oneshot::channel();
529        self.network.send_request(peer_id, request(tx));
530        rx.await.unwrap_or(Err(RequestError::ChannelClosed))
531    }
532}
533
534// === impl PeerConfig ===
535
536impl<C> PeerConfig<C> {
537    /// Creates a config for a peer with a random secret key that serves data from `client`.
538    ///
539    /// The peer's devp2p and discovery bind to any available port.
540    pub fn new(client: C) -> Self {
541        Self {
542            client,
543            secret_key: rng_secret_key(),
544            protocols: None,
545            peers_config: PeersConfig::test(),
546        }
547    }
548
549    /// Sets the secret key, which determines the peer's [`PeerId`].
550    pub const fn with_secret_key(mut self, secret_key: SecretKey) -> Self {
551        self.secret_key = secret_key;
552        self
553    }
554
555    /// Sets the protocols advertised in the hello message, e.g. the supported eth versions.
556    pub fn with_protocols(mut self, protocols: impl IntoIterator<Item: Into<Protocol>>) -> Self {
557        self.protocols = Some(protocols.into_iter().map(Into::into).collect());
558        self
559    }
560
561    /// Sets the [`PeersConfig`], which defaults to [`PeersConfig::test`].
562    pub fn with_peers_config(mut self, peers_config: PeersConfig) -> Self {
563        self.peers_config = peers_config;
564        self
565    }
566
567    /// Launches the network and returns the [`Peer`] that manages it.
568    pub async fn launch(self) -> Result<Peer<C>, NetworkError>
569    where
570        C: BlockNumReader + ChainSpecProvider<ChainSpec: Hardforks> + Clone + 'static,
571    {
572        let Self { client, secret_key, protocols, peers_config } = self;
573        let mut builder = NetworkConfigBuilder::new(secret_key, Runtime::test())
574            .listener_addr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)))
575            .discovery_addr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)))
576            .disable_dns_discovery()
577            .disable_discv4_discovery()
578            .peer_config(peers_config);
579        if let Some(protocols) = protocols {
580            // `NetworkConfigBuilder::build` re-derives snap advertisement from `snap_enabled`,
581            // which would otherwise silently strip a manually included `snap` capability.
582            let snap_enabled = protocols.iter().any(|p| p.cap.name == Protocol::snap_2().cap.name);
583            let hello_message = HelloMessageWithProtocols::builder(builder.get_peer_id())
584                .protocols(protocols)
585                .build();
586            builder = builder.with_snap(snap_enabled).hello_message(hello_message);
587        }
588
589        let network = NetworkManager::new(builder.build(client.clone())).await?;
590        Ok(Peer { network, client, request_handler: None, transactions_manager: None, pool: None })
591    }
592}
593
594impl Default for PeerConfig {
595    fn default() -> Self {
596        Self::new(NoopProvider::default())
597    }
598}
599
600/// A helper type to await network events
601///
602/// This makes it easier to await established connections
603#[derive(Debug)]
604pub struct NetworkEventStream {
605    inner: EventStream<NetworkEvent>,
606}
607
608// === impl NetworkEventStream ===
609
610impl NetworkEventStream {
611    /// Create a new [`NetworkEventStream`] from the given network event receiver stream.
612    pub const fn new(inner: EventStream<NetworkEvent>) -> Self {
613        Self { inner }
614    }
615
616    /// Awaits the next event for a session to be closed
617    pub async fn next_session_closed(&mut self) -> Option<(PeerId, Option<DisconnectReason>)> {
618        while let Some(ev) = self.inner.next().await {
619            if let NetworkEvent::Peer(PeerEvent::SessionClosed { peer_id, reason }) = ev {
620                return Some((peer_id, reason))
621            }
622        }
623        None
624    }
625
626    /// Awaits the next event for an established session
627    pub async fn next_session_established(&mut self) -> Option<PeerId> {
628        while let Some(ev) = self.inner.next().await {
629            match ev {
630                NetworkEvent::ActivePeerSession { info, .. } |
631                NetworkEvent::Peer(PeerEvent::SessionEstablished(info)) => {
632                    return Some(info.peer_id)
633                }
634                _ => {}
635            }
636        }
637        None
638    }
639
640    /// Awaits the next `num` events for an established session
641    pub async fn take_session_established(&mut self, mut num: usize) -> Vec<PeerId> {
642        if num == 0 {
643            return Vec::new();
644        }
645        let mut peers = Vec::with_capacity(num);
646        while let Some(ev) = self.inner.next().await {
647            if let NetworkEvent::ActivePeerSession { info: SessionInfo { peer_id, .. }, .. } = ev {
648                peers.push(peer_id);
649                num -= 1;
650                if num == 0 {
651                    return peers;
652                }
653            }
654        }
655        peers
656    }
657
658    /// Returns the peer of the next event if it is a [`PeerEvent::PeerAdded`].
659    pub async fn peer_added(&mut self) -> Option<PeerId> {
660        match self.inner.next().await {
661            Some(NetworkEvent::Peer(PeerEvent::PeerAdded(peer_id))) => Some(peer_id),
662            _ => None,
663        }
664    }
665
666    /// Returns the peer of the next event if it is a [`PeerEvent::PeerRemoved`].
667    pub async fn peer_removed(&mut self) -> Option<PeerId> {
668        match self.inner.next().await {
669            Some(NetworkEvent::Peer(PeerEvent::PeerRemoved(peer_id))) => Some(peer_id),
670            _ => None,
671        }
672    }
673}
674
675/// A provider that can serve all eth requests of a [`Peer`] in a spawned [`Testnet`].
676pub trait TestnetProvider:
677    BlockReader<
678        Block = reth_ethereum_primitives::Block,
679        Receipt = reth_ethereum_primitives::Receipt,
680        Header = alloy_consensus::Header,
681    > + BalProvider
682    + StateProviderFactory
683    + StateRangeProviderFactory
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        + Unpin
698        + 'static
699{
700}
701
702/// A transaction pool that can back the transactions manager of a [`Peer`] in a spawned
703/// [`Testnet`].
704pub trait TestnetPool:
705    TransactionPool<
706        Transaction: PoolTransaction<
707            Consensus = TransactionSigned,
708            Pooled = PooledTransactionVariant,
709        >,
710    > + Unpin
711    + 'static
712{
713}
714
715impl<T> TestnetPool for T where
716    T: TransactionPool<
717            Transaction: PoolTransaction<
718                Consensus = TransactionSigned,
719                Pooled = PooledTransactionVariant,
720            >,
721        > + Unpin
722        + 'static
723{
724}
725
726/// Creates a transactions manager for `pool` that is connected to `network`.
727fn new_transactions_manager<Pool: TransactionPool>(
728    network: &mut NetworkManager<EthNetworkPrimitives>,
729    pool: Pool,
730    config: TransactionsManagerConfig,
731    policy: TransactionPropagationKind,
732) -> TransactionsManager<Pool, EthNetworkPrimitives> {
733    let (tx, rx) =
734        memory_bounded_channel(DEFAULT_TX_MANAGER_CHANNEL_MEMORY_LIMIT_BYTES, "test_tx_channel");
735    network.set_transactions(tx);
736    let policies = NetworkPolicies::new(policy, StrictEthAnnouncementFilter::default());
737    TransactionsManager::with_policy(network.handle().clone(), pool, rx, config, policies)
738}