reth_e2e_test_utils/
lib.rs

1//! Utilities for end-to-end tests.
2
3use node::NodeTestContext;
4use reth_chainspec::{ChainSpec, EthChainSpec};
5use reth_db::{test_utils::TempDatabase, DatabaseEnv};
6use reth_engine_local::LocalPayloadAttributesBuilder;
7use reth_network_api::test_utils::PeersHandleProvider;
8use reth_node_builder::{
9    components::NodeComponentsBuilder,
10    rpc::{EngineValidatorAddOn, RethRpcAddOns},
11    EngineNodeLauncher, FullNodeTypesAdapter, Node, NodeAdapter, NodeBuilder, NodeComponents,
12    NodeConfig, NodeHandle, NodePrimitives, NodeTypes, NodeTypesWithDBAdapter,
13    PayloadAttributesBuilder, PayloadTypes,
14};
15use reth_node_core::args::{DiscoveryArgs, NetworkArgs, RpcServerArgs};
16use reth_provider::providers::{BlockchainProvider, NodeTypesForProvider};
17use reth_rpc_server_types::RpcModuleSelection;
18use reth_tasks::TaskManager;
19use std::sync::Arc;
20use tracing::{span, Level};
21use wallet::Wallet;
22
23/// Wrapper type to create test nodes
24pub mod node;
25pub mod testsuite;
26
27/// Helper for transaction operations
28pub mod transaction;
29
30/// Helper type to yield accounts from mnemonic
31pub mod wallet;
32
33/// Helper for payload operations
34mod payload;
35
36/// Helper for setting up nodes with pre-imported chain data
37pub mod setup_import;
38
39/// Helper for network operations
40mod network;
41
42/// Helper for rpc operations
43mod rpc;
44
45/// Utilities for creating and writing RLP test data
46pub mod test_rlp_utils;
47
48/// Creates the initial setup with `num_nodes` started and interconnected.
49pub async fn setup<N>(
50    num_nodes: usize,
51    chain_spec: Arc<N::ChainSpec>,
52    is_dev: bool,
53    attributes_generator: impl Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadBuilderAttributes + Send + Sync + Copy + 'static,
54) -> eyre::Result<(Vec<NodeHelperType<N>>, TaskManager, Wallet)>
55where
56    N: Default + Node<TmpNodeAdapter<N>> + NodeTypesForProvider,
57    N::ComponentsBuilder: NodeComponentsBuilder<
58        TmpNodeAdapter<N>,
59        Components: NodeComponents<TmpNodeAdapter<N>, Network: PeersHandleProvider>,
60    >,
61    N::AddOns: RethRpcAddOns<Adapter<N>> + EngineValidatorAddOn<Adapter<N>>,
62    LocalPayloadAttributesBuilder<N::ChainSpec>:
63        PayloadAttributesBuilder<<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes>,
64{
65    let tasks = TaskManager::current();
66    let exec = tasks.executor();
67
68    let network_config = NetworkArgs {
69        discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() },
70        ..NetworkArgs::default()
71    };
72
73    // Create nodes and peer them
74    let mut nodes: Vec<NodeTestContext<_, _>> = Vec::with_capacity(num_nodes);
75
76    for idx in 0..num_nodes {
77        let node_config = NodeConfig::new(chain_spec.clone())
78            .with_network(network_config.clone())
79            .with_unused_ports()
80            .with_rpc(RpcServerArgs::default().with_unused_ports().with_http())
81            .set_dev(is_dev);
82
83        let span = span!(Level::INFO, "node", idx);
84        let _enter = span.enter();
85        let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config.clone())
86            .testing_node(exec.clone())
87            .node(Default::default())
88            .launch()
89            .await?;
90
91        let mut node = NodeTestContext::new(node, attributes_generator).await?;
92
93        // Connect each node in a chain.
94        if let Some(previous_node) = nodes.last_mut() {
95            previous_node.connect(&mut node).await;
96        }
97
98        // Connect last node with the first if there are more than two
99        if idx + 1 == num_nodes && num_nodes > 2 {
100            if let Some(first_node) = nodes.first_mut() {
101                node.connect(first_node).await;
102            }
103        }
104
105        nodes.push(node);
106    }
107
108    Ok((nodes, tasks, Wallet::default().with_chain_id(chain_spec.chain().into())))
109}
110
111/// Creates the initial setup with `num_nodes` started and interconnected.
112pub async fn setup_engine<N>(
113    num_nodes: usize,
114    chain_spec: Arc<N::ChainSpec>,
115    is_dev: bool,
116    tree_config: reth_node_api::TreeConfig,
117    attributes_generator: impl Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadBuilderAttributes + Send + Sync + Copy + 'static,
118) -> eyre::Result<(
119    Vec<NodeHelperType<N, BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>>>,
120    TaskManager,
121    Wallet,
122)>
123where
124    N: NodeBuilderHelper,
125    LocalPayloadAttributesBuilder<N::ChainSpec>:
126        PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
127{
128    setup_engine_with_connection::<N>(
129        num_nodes,
130        chain_spec,
131        is_dev,
132        tree_config,
133        attributes_generator,
134        true,
135    )
136    .await
137}
138
139/// Creates the initial setup with `num_nodes` started and optionally interconnected.
140pub async fn setup_engine_with_connection<N>(
141    num_nodes: usize,
142    chain_spec: Arc<N::ChainSpec>,
143    is_dev: bool,
144    tree_config: reth_node_api::TreeConfig,
145    attributes_generator: impl Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadBuilderAttributes + Send + Sync + Copy + 'static,
146    connect_nodes: bool,
147) -> eyre::Result<(
148    Vec<NodeHelperType<N, BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>>>,
149    TaskManager,
150    Wallet,
151)>
152where
153    N: NodeBuilderHelper,
154    LocalPayloadAttributesBuilder<N::ChainSpec>:
155        PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
156{
157    let tasks = TaskManager::current();
158    let exec = tasks.executor();
159
160    let network_config = NetworkArgs {
161        discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() },
162        ..NetworkArgs::default()
163    };
164
165    // Create nodes and peer them
166    let mut nodes: Vec<NodeTestContext<_, _>> = Vec::with_capacity(num_nodes);
167
168    for idx in 0..num_nodes {
169        let node_config = NodeConfig::new(chain_spec.clone())
170            .with_network(network_config.clone())
171            .with_unused_ports()
172            .with_rpc(
173                RpcServerArgs::default()
174                    .with_unused_ports()
175                    .with_http()
176                    .with_http_api(RpcModuleSelection::All),
177            )
178            .set_dev(is_dev);
179
180        let span = span!(Level::INFO, "node", idx);
181        let _enter = span.enter();
182        let node = N::default();
183        let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config.clone())
184            .testing_node(exec.clone())
185            .with_types_and_provider::<N, BlockchainProvider<_>>()
186            .with_components(node.components_builder())
187            .with_add_ons(node.add_ons())
188            .launch_with_fn(|builder| {
189                let launcher = EngineNodeLauncher::new(
190                    builder.task_executor().clone(),
191                    builder.config().datadir(),
192                    tree_config.clone(),
193                );
194                builder.launch_with(launcher)
195            })
196            .await?;
197
198        let mut node = NodeTestContext::new(node, attributes_generator).await?;
199
200        let genesis = node.block_hash(0);
201        node.update_forkchoice(genesis, genesis).await?;
202
203        // Connect each node in a chain if requested.
204        if connect_nodes {
205            if let Some(previous_node) = nodes.last_mut() {
206                previous_node.connect(&mut node).await;
207            }
208
209            // Connect last node with the first if there are more than two
210            if idx + 1 == num_nodes && num_nodes > 2 {
211                if let Some(first_node) = nodes.first_mut() {
212                    node.connect(first_node).await;
213                }
214            }
215        }
216
217        nodes.push(node);
218    }
219
220    Ok((nodes, tasks, Wallet::default().with_chain_id(chain_spec.chain().into())))
221}
222
223// Type aliases
224
225/// Testing database
226pub type TmpDB = Arc<TempDatabase<DatabaseEnv>>;
227type TmpNodeAdapter<N, Provider = BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>> =
228    FullNodeTypesAdapter<N, TmpDB, Provider>;
229
230/// Type alias for a `NodeAdapter`
231pub type Adapter<N, Provider = BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>> = NodeAdapter<
232    TmpNodeAdapter<N, Provider>,
233    <<N as Node<TmpNodeAdapter<N, Provider>>>::ComponentsBuilder as NodeComponentsBuilder<
234        TmpNodeAdapter<N, Provider>,
235    >>::Components,
236>;
237
238/// Type alias for a type of `NodeHelper`
239pub type NodeHelperType<N, Provider = BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>> =
240    NodeTestContext<Adapter<N, Provider>, <N as Node<TmpNodeAdapter<N, Provider>>>::AddOns>;
241
242/// Helper trait to simplify bounds when calling setup functions.
243pub trait NodeBuilderHelper
244where
245    Self: Default
246        + NodeTypesForProvider<
247            Payload: PayloadTypes<
248                PayloadBuilderAttributes: From<reth_payload_builder::EthPayloadBuilderAttributes>,
249            >,
250        > + Node<
251            TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
252            Primitives: NodePrimitives<
253                BlockHeader = alloy_consensus::Header,
254                BlockBody = alloy_consensus::BlockBody<
255                    <Self::Primitives as NodePrimitives>::SignedTx,
256                >,
257            >,
258            ComponentsBuilder: NodeComponentsBuilder<
259                TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
260                Components: NodeComponents<
261                    TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
262                    Network: PeersHandleProvider,
263                >,
264            >,
265            AddOns: RethRpcAddOns<
266                Adapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
267            > + EngineValidatorAddOn<
268                Adapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
269            >,
270            ChainSpec: From<ChainSpec> + Clone,
271        >,
272    LocalPayloadAttributesBuilder<Self::ChainSpec>:
273        PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes>,
274{
275}
276
277impl<T> NodeBuilderHelper for T
278where
279    Self: Default
280        + NodeTypesForProvider<
281            Payload: PayloadTypes<
282                PayloadBuilderAttributes: From<reth_payload_builder::EthPayloadBuilderAttributes>,
283            >,
284        > + Node<
285            TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
286            Primitives: NodePrimitives<
287                BlockHeader = alloy_consensus::Header,
288                BlockBody = alloy_consensus::BlockBody<
289                    <Self::Primitives as NodePrimitives>::SignedTx,
290                >,
291            >,
292            ComponentsBuilder: NodeComponentsBuilder<
293                TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
294                Components: NodeComponents<
295                    TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
296                    Network: PeersHandleProvider,
297                >,
298            >,
299            AddOns: RethRpcAddOns<
300                Adapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
301            > + EngineValidatorAddOn<
302                Adapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
303            >,
304            ChainSpec: From<ChainSpec> + Clone,
305        >,
306    LocalPayloadAttributesBuilder<Self::ChainSpec>:
307        PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes>,
308{
309}