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_primitives_traits::AlloyBlockHeader;
15use reth_provider::providers::BlockchainProvider;
16use reth_rpc_server_types::RpcModuleSelection;
17use reth_tasks::Runtime;
18use std::sync::Arc;
19use tracing::{span, Instrument, Level};
20
21type TreeConfigModifier =
23 Box<dyn Fn(reth_node_api::TreeConfig) -> reth_node_api::TreeConfig + Send + Sync>;
24
25type NodeConfigModifier<C> = Box<dyn Fn(NodeConfig<C>) -> NodeConfig<C> + Send + Sync>;
27
28pub struct E2ETestSetupBuilder<N, F>
34where
35 N: NodeBuilderHelper,
36 F: Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes
37 + Send
38 + Sync
39 + Copy
40 + 'static,
41{
42 num_nodes: usize,
43 chain_spec: Arc<N::ChainSpec>,
44 attributes_generator: F,
45 connect_nodes: bool,
46 tree_config_modifier: Option<TreeConfigModifier>,
47 node_config_modifier: Option<NodeConfigModifier<N::ChainSpec>>,
48}
49
50impl<N, F> E2ETestSetupBuilder<N, F>
51where
52 N: NodeBuilderHelper,
53 F: Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes
54 + Send
55 + Sync
56 + Copy
57 + 'static,
58{
59 pub fn new(num_nodes: usize, chain_spec: Arc<N::ChainSpec>, attributes_generator: F) -> Self {
61 Self {
62 num_nodes,
63 chain_spec,
64 attributes_generator,
65 connect_nodes: true,
66 tree_config_modifier: None,
67 node_config_modifier: None,
68 }
69 }
70
71 pub const fn with_connect_nodes(mut self, connect_nodes: bool) -> Self {
73 self.connect_nodes = connect_nodes;
74 self
75 }
76
77 pub fn with_tree_config_modifier<G>(mut self, modifier: G) -> Self
81 where
82 G: Fn(reth_node_api::TreeConfig) -> reth_node_api::TreeConfig + Send + Sync + 'static,
83 {
84 self.tree_config_modifier = Some(Box::new(modifier));
85 self
86 }
87
88 pub fn with_node_config_modifier<G>(mut self, modifier: G) -> Self
92 where
93 G: Fn(NodeConfig<N::ChainSpec>) -> NodeConfig<N::ChainSpec> + Send + Sync + 'static,
94 {
95 self.node_config_modifier = Some(Box::new(modifier));
96 self
97 }
98
99 pub fn with_pruning(self, pruning: reth_node_core::args::PruningArgs) -> Self {
101 self.with_node_config_modifier(move |config| config.with_pruning(pruning.clone()))
102 }
103
104 pub fn with_storage_v2(self) -> Self {
107 self.with_node_config_modifier(|mut config| {
108 config.storage.v2 = true;
109 config
110 })
111 }
112
113 pub async fn build(
115 self,
116 ) -> eyre::Result<(
117 Vec<NodeHelperType<N, BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>>>,
118 Wallet,
119 )> {
120 let runtime = Runtime::test();
121
122 let network_config = NetworkArgs {
123 discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() },
124 ..NetworkArgs::default()
125 };
126
127 let base_tree_config =
129 reth_node_api::TreeConfig::default().with_cross_block_cache_size(1024 * 1024);
130 let tree_config = if let Some(modifier) = self.tree_config_modifier {
131 modifier(base_tree_config)
132 } else {
133 base_tree_config
134 };
135
136 let mut nodes = (0..self.num_nodes)
137 .map(async |idx| {
138 let base_config = NodeConfig::new(self.chain_spec.clone())
140 .with_network(network_config.clone())
141 .with_unused_ports()
142 .with_rpc(
143 RpcServerArgs::default()
144 .with_unused_ports()
145 .with_http()
146 .with_http_api(RpcModuleSelection::All),
147 );
148
149 let node_config = if let Some(modifier) = &self.node_config_modifier {
151 modifier(base_config)
152 } else {
153 base_config
154 };
155
156 let span = span!(Level::INFO, "node", idx);
157 let node = N::default();
158 let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
159 .testing_node(runtime.clone())
160 .with_types_and_provider::<N, BlockchainProvider<_>>()
161 .with_components(node.components_builder())
162 .with_add_ons(node.add_ons())
163 .launch_with_fn(|builder| {
164 let launcher = EngineNodeLauncher::new(
165 builder.task_executor().clone(),
166 builder.config().datadir(),
167 tree_config.clone(),
168 );
169 builder.launch_with(launcher)
170 })
171 .instrument(span)
172 .await?;
173
174 let node = NodeTestContext::new(node, self.attributes_generator).await?;
175 let genesis_number = self.chain_spec.genesis_header().number();
176 let genesis = node.block_hash(genesis_number);
177 node.update_forkchoice(genesis, genesis).await?;
178
179 eyre::Ok(node)
180 })
181 .collect::<TryJoinAll<_>>()
182 .await?;
183
184 for idx in 0..self.num_nodes {
185 let (prev, current) = nodes.split_at_mut(idx);
186 let current = current.first_mut().unwrap();
187 if self.connect_nodes {
189 if let Some(prev_idx) = idx.checked_sub(1) {
190 prev[prev_idx].connect(current).await;
191 }
192
193 if idx + 1 == self.num_nodes &&
195 self.num_nodes > 2 &&
196 let Some(first) = prev.first_mut()
197 {
198 current.connect(first).await;
199 }
200 }
201 }
202
203 Ok((nodes, Wallet::default().with_chain_id(self.chain_spec.chain().into())))
204 }
205}
206
207impl<N, F> std::fmt::Debug for E2ETestSetupBuilder<N, F>
208where
209 N: NodeBuilderHelper,
210 F: Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes
211 + Send
212 + Sync
213 + Copy
214 + 'static,
215{
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 f.debug_struct("E2ETestSetupBuilder")
218 .field("num_nodes", &self.num_nodes)
219 .field("connect_nodes", &self.connect_nodes)
220 .field("tree_config_modifier", &self.tree_config_modifier.as_ref().map(|_| "<closure>"))
221 .field("node_config_modifier", &self.node_config_modifier.as_ref().map(|_| "<closure>"))
222 .finish_non_exhaustive()
223 }
224}