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 &&
100            num_nodes > 2 &&
101            let Some(first_node) = nodes.first_mut()
102        {
103            node.connect(first_node).await;
104        }
105
106        nodes.push(node);
107    }
108
109    Ok((nodes, tasks, Wallet::default().with_chain_id(chain_spec.chain().into())))
110}
111
112/// Creates the initial setup with `num_nodes` started and interconnected.
113pub async fn setup_engine<N>(
114    num_nodes: usize,
115    chain_spec: Arc<N::ChainSpec>,
116    is_dev: bool,
117    tree_config: reth_node_api::TreeConfig,
118    attributes_generator: impl Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadBuilderAttributes + Send + Sync + Copy + 'static,
119) -> eyre::Result<(
120    Vec<NodeHelperType<N, BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>>>,
121    TaskManager,
122    Wallet,
123)>
124where
125    N: NodeBuilderHelper,
126    LocalPayloadAttributesBuilder<N::ChainSpec>:
127        PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
128{
129    setup_engine_with_connection::<N>(
130        num_nodes,
131        chain_spec,
132        is_dev,
133        tree_config,
134        attributes_generator,
135        true,
136    )
137    .await
138}
139
140/// Creates the initial setup with `num_nodes` started and optionally interconnected.
141pub async fn setup_engine_with_connection<N>(
142    num_nodes: usize,
143    chain_spec: Arc<N::ChainSpec>,
144    is_dev: bool,
145    tree_config: reth_node_api::TreeConfig,
146    attributes_generator: impl Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadBuilderAttributes + Send + Sync + Copy + 'static,
147    connect_nodes: bool,
148) -> eyre::Result<(
149    Vec<NodeHelperType<N, BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>>>,
150    TaskManager,
151    Wallet,
152)>
153where
154    N: NodeBuilderHelper,
155    LocalPayloadAttributesBuilder<N::ChainSpec>:
156        PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
157{
158    let tasks = TaskManager::current();
159    let exec = tasks.executor();
160
161    let network_config = NetworkArgs {
162        discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() },
163        ..NetworkArgs::default()
164    };
165
166    // Create nodes and peer them
167    let mut nodes: Vec<NodeTestContext<_, _>> = Vec::with_capacity(num_nodes);
168
169    for idx in 0..num_nodes {
170        let node_config = NodeConfig::new(chain_spec.clone())
171            .with_network(network_config.clone())
172            .with_unused_ports()
173            .with_rpc(
174                RpcServerArgs::default()
175                    .with_unused_ports()
176                    .with_http()
177                    .with_http_api(RpcModuleSelection::All),
178            )
179            .set_dev(is_dev);
180
181        let span = span!(Level::INFO, "node", idx);
182        let _enter = span.enter();
183        let node = N::default();
184        let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config.clone())
185            .testing_node(exec.clone())
186            .with_types_and_provider::<N, BlockchainProvider<_>>()
187            .with_components(node.components_builder())
188            .with_add_ons(node.add_ons())
189            .launch_with_fn(|builder| {
190                let launcher = EngineNodeLauncher::new(
191                    builder.task_executor().clone(),
192                    builder.config().datadir(),
193                    tree_config.clone(),
194                );
195                builder.launch_with(launcher)
196            })
197            .await?;
198
199        let mut node = NodeTestContext::new(node, attributes_generator).await?;
200
201        let genesis = node.block_hash(0);
202        node.update_forkchoice(genesis, genesis).await?;
203
204        // Connect each node in a chain if requested.
205        if connect_nodes {
206            if let Some(previous_node) = nodes.last_mut() {
207                previous_node.connect(&mut node).await;
208            }
209
210            // Connect last node with the first if there are more than two
211            if idx + 1 == num_nodes &&
212                num_nodes > 2 &&
213                let Some(first_node) = nodes.first_mut()
214            {
215                node.connect(first_node).await;
216            }
217        }
218
219        nodes.push(node);
220    }
221
222    Ok((nodes, tasks, Wallet::default().with_chain_id(chain_spec.chain().into())))
223}
224
225// Type aliases
226
227/// Testing database
228pub type TmpDB = Arc<TempDatabase<DatabaseEnv>>;
229type TmpNodeAdapter<N, Provider = BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>> =
230    FullNodeTypesAdapter<N, TmpDB, Provider>;
231
232/// Type alias for a `NodeAdapter`
233pub type Adapter<N, Provider = BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>> = NodeAdapter<
234    TmpNodeAdapter<N, Provider>,
235    <<N as Node<TmpNodeAdapter<N, Provider>>>::ComponentsBuilder as NodeComponentsBuilder<
236        TmpNodeAdapter<N, Provider>,
237    >>::Components,
238>;
239
240/// Type alias for a type of `NodeHelper`
241pub type NodeHelperType<N, Provider = BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>> =
242    NodeTestContext<Adapter<N, Provider>, <N as Node<TmpNodeAdapter<N, Provider>>>::AddOns>;
243
244/// Helper trait to simplify bounds when calling setup functions.
245pub trait NodeBuilderHelper
246where
247    Self: Default
248        + NodeTypesForProvider<
249            Payload: PayloadTypes<
250                PayloadBuilderAttributes: From<reth_payload_builder::EthPayloadBuilderAttributes>,
251            >,
252        > + Node<
253            TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
254            Primitives: NodePrimitives<
255                BlockHeader = alloy_consensus::Header,
256                BlockBody = alloy_consensus::BlockBody<
257                    <Self::Primitives as NodePrimitives>::SignedTx,
258                >,
259            >,
260            ComponentsBuilder: NodeComponentsBuilder<
261                TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
262                Components: NodeComponents<
263                    TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
264                    Network: PeersHandleProvider,
265                >,
266            >,
267            AddOns: RethRpcAddOns<
268                Adapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
269            > + EngineValidatorAddOn<
270                Adapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
271            >,
272            ChainSpec: From<ChainSpec> + Clone,
273        >,
274    LocalPayloadAttributesBuilder<Self::ChainSpec>:
275        PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes>,
276{
277}
278
279impl<T> NodeBuilderHelper for T
280where
281    Self: Default
282        + NodeTypesForProvider<
283            Payload: PayloadTypes<
284                PayloadBuilderAttributes: From<reth_payload_builder::EthPayloadBuilderAttributes>,
285            >,
286        > + Node<
287            TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
288            Primitives: NodePrimitives<
289                BlockHeader = alloy_consensus::Header,
290                BlockBody = alloy_consensus::BlockBody<
291                    <Self::Primitives as NodePrimitives>::SignedTx,
292                >,
293            >,
294            ComponentsBuilder: NodeComponentsBuilder<
295                TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
296                Components: NodeComponents<
297                    TmpNodeAdapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
298                    Network: PeersHandleProvider,
299                >,
300            >,
301            AddOns: RethRpcAddOns<
302                Adapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
303            > + EngineValidatorAddOn<
304                Adapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
305            >,
306            ChainSpec: From<ChainSpec> + Clone,
307        >,
308    LocalPayloadAttributesBuilder<Self::ChainSpec>:
309        PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes>,
310{
311}