reth_e2e_test_utils/
setup_builder.rs1use crate::{node::NodeTestContext, wallet::Wallet, NodeBuilderHelper, NodeHelperType, TmpDB};
7use futures_util::future::TryJoinAll;
8use reth_chainspec::EthChainSpec;
9use reth_node_builder::{
10 EngineNodeLauncher, NodeBuilder, NodeConfig, NodeHandle, NodeTypes, NodeTypesWithDBAdapter,
11 PayloadTypes,
12};
13use reth_node_core::args::{DiscoveryArgs, NetworkArgs, RpcServerArgs};
14use reth_provider::providers::BlockchainProvider;
15use reth_rpc_server_types::RpcModuleSelection;
16use reth_tasks::TaskManager;
17use std::sync::Arc;
18use tracing::{span, Instrument, Level};
19
20type TreeConfigModifier =
22 Box<dyn Fn(reth_node_api::TreeConfig) -> reth_node_api::TreeConfig + Send + Sync>;
23
24type NodeConfigModifier<C> = Box<dyn Fn(NodeConfig<C>) -> NodeConfig<C> + Send + Sync>;
26
27pub struct E2ETestSetupBuilder<N, F>
33where
34 N: NodeBuilderHelper,
35 F: Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadBuilderAttributes
36 + Send
37 + Sync
38 + Copy
39 + 'static,
40{
41 num_nodes: usize,
42 chain_spec: Arc<N::ChainSpec>,
43 attributes_generator: F,
44 connect_nodes: bool,
45 tree_config_modifier: Option<TreeConfigModifier>,
46 node_config_modifier: Option<NodeConfigModifier<N::ChainSpec>>,
47}
48
49impl<N, F> E2ETestSetupBuilder<N, F>
50where
51 N: NodeBuilderHelper,
52 F: Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadBuilderAttributes
53 + Send
54 + Sync
55 + Copy
56 + 'static,
57{
58 pub fn new(num_nodes: usize, chain_spec: Arc<N::ChainSpec>, attributes_generator: F) -> Self {
60 Self {
61 num_nodes,
62 chain_spec,
63 attributes_generator,
64 connect_nodes: true,
65 tree_config_modifier: None,
66 node_config_modifier: None,
67 }
68 }
69
70 pub const fn with_connect_nodes(mut self, connect_nodes: bool) -> Self {
72 self.connect_nodes = connect_nodes;
73 self
74 }
75
76 pub fn with_tree_config_modifier<G>(mut self, modifier: G) -> Self
80 where
81 G: Fn(reth_node_api::TreeConfig) -> reth_node_api::TreeConfig + Send + Sync + 'static,
82 {
83 self.tree_config_modifier = Some(Box::new(modifier));
84 self
85 }
86
87 pub fn with_node_config_modifier<G>(mut self, modifier: G) -> Self
91 where
92 G: Fn(NodeConfig<N::ChainSpec>) -> NodeConfig<N::ChainSpec> + Send + Sync + 'static,
93 {
94 self.node_config_modifier = Some(Box::new(modifier));
95 self
96 }
97
98 pub async fn build(
100 self,
101 ) -> eyre::Result<(
102 Vec<NodeHelperType<N, BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>>>,
103 TaskManager,
104 Wallet,
105 )> {
106 let tasks = TaskManager::current();
107 let exec = tasks.executor();
108
109 let network_config = NetworkArgs {
110 discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() },
111 ..NetworkArgs::default()
112 };
113
114 let tree_config = if let Some(modifier) = self.tree_config_modifier {
116 modifier(reth_node_api::TreeConfig::default())
117 } else {
118 reth_node_api::TreeConfig::default()
119 };
120
121 let mut nodes = (0..self.num_nodes)
122 .map(async |idx| {
123 let base_config = NodeConfig::new(self.chain_spec.clone())
125 .with_network(network_config.clone())
126 .with_unused_ports()
127 .with_rpc(
128 RpcServerArgs::default()
129 .with_unused_ports()
130 .with_http()
131 .with_http_api(RpcModuleSelection::All),
132 );
133
134 let node_config = if let Some(modifier) = &self.node_config_modifier {
136 modifier(base_config)
137 } else {
138 base_config
139 };
140
141 let span = span!(Level::INFO, "node", idx);
142 let node = N::default();
143 let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
144 .testing_node(exec.clone())
145 .with_types_and_provider::<N, BlockchainProvider<_>>()
146 .with_components(node.components_builder())
147 .with_add_ons(node.add_ons())
148 .launch_with_fn(|builder| {
149 let launcher = EngineNodeLauncher::new(
150 builder.task_executor().clone(),
151 builder.config().datadir(),
152 tree_config.clone(),
153 );
154 builder.launch_with(launcher)
155 })
156 .instrument(span)
157 .await?;
158
159 let node = NodeTestContext::new(node, self.attributes_generator).await?;
160
161 let genesis = node.block_hash(0);
162 node.update_forkchoice(genesis, genesis).await?;
163
164 eyre::Ok(node)
165 })
166 .collect::<TryJoinAll<_>>()
167 .await?;
168
169 for idx in 0..self.num_nodes {
170 let (prev, current) = nodes.split_at_mut(idx);
171 let current = current.first_mut().unwrap();
172 if self.connect_nodes {
174 if let Some(prev_idx) = idx.checked_sub(1) {
175 prev[prev_idx].connect(current).await;
176 }
177
178 if idx + 1 == self.num_nodes &&
180 self.num_nodes > 2 &&
181 let Some(first) = prev.first_mut()
182 {
183 current.connect(first).await;
184 }
185 }
186 }
187
188 Ok((nodes, tasks, Wallet::default().with_chain_id(self.chain_spec.chain().into())))
189 }
190}
191
192impl<N, F> std::fmt::Debug for E2ETestSetupBuilder<N, F>
193where
194 N: NodeBuilderHelper,
195 F: Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadBuilderAttributes
196 + Send
197 + Sync
198 + Copy
199 + 'static,
200{
201 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202 f.debug_struct("E2ETestSetupBuilder")
203 .field("num_nodes", &self.num_nodes)
204 .field("connect_nodes", &self.connect_nodes)
205 .field("tree_config_modifier", &self.tree_config_modifier.as_ref().map(|_| "<closure>"))
206 .field("node_config_modifier", &self.node_config_modifier.as_ref().map(|_| "<closure>"))
207 .finish_non_exhaustive()
208 }
209}