reth_e2e_test_utils/
setup_builder.rs1use crate::{node::NodeTestContext, wallet::Wallet, NodeBuilderHelper, NodeHelperType, TmpDB};
7use reth_chainspec::EthChainSpec;
8use reth_engine_local::LocalPayloadAttributesBuilder;
9use reth_node_builder::{
10 EngineNodeLauncher, NodeBuilder, NodeConfig, NodeHandle, NodeTypes, NodeTypesWithDBAdapter,
11 PayloadAttributesBuilder, 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, 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 LocalPayloadAttributesBuilder<N::ChainSpec>:
41 PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
42{
43 num_nodes: usize,
44 chain_spec: Arc<N::ChainSpec>,
45 attributes_generator: F,
46 connect_nodes: bool,
47 tree_config_modifier: Option<TreeConfigModifier>,
48 node_config_modifier: Option<NodeConfigModifier<N::ChainSpec>>,
49}
50
51impl<N, F> E2ETestSetupBuilder<N, F>
52where
53 N: NodeBuilderHelper,
54 F: Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadBuilderAttributes
55 + Send
56 + Sync
57 + Copy
58 + 'static,
59 LocalPayloadAttributesBuilder<N::ChainSpec>:
60 PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
61{
62 pub fn new(num_nodes: usize, chain_spec: Arc<N::ChainSpec>, attributes_generator: F) -> Self {
64 Self {
65 num_nodes,
66 chain_spec,
67 attributes_generator,
68 connect_nodes: true,
69 tree_config_modifier: None,
70 node_config_modifier: None,
71 }
72 }
73
74 pub const fn with_connect_nodes(mut self, connect_nodes: bool) -> Self {
76 self.connect_nodes = connect_nodes;
77 self
78 }
79
80 pub fn with_tree_config_modifier<G>(mut self, modifier: G) -> Self
84 where
85 G: Fn(reth_node_api::TreeConfig) -> reth_node_api::TreeConfig + Send + Sync + 'static,
86 {
87 self.tree_config_modifier = Some(Box::new(modifier));
88 self
89 }
90
91 pub fn with_node_config_modifier<G>(mut self, modifier: G) -> Self
95 where
96 G: Fn(NodeConfig<N::ChainSpec>) -> NodeConfig<N::ChainSpec> + Send + Sync + 'static,
97 {
98 self.node_config_modifier = Some(Box::new(modifier));
99 self
100 }
101
102 pub async fn build(
104 self,
105 ) -> eyre::Result<(
106 Vec<NodeHelperType<N, BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>>>,
107 TaskManager,
108 Wallet,
109 )> {
110 let tasks = TaskManager::current();
111 let exec = tasks.executor();
112
113 let network_config = NetworkArgs {
114 discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() },
115 ..NetworkArgs::default()
116 };
117
118 let tree_config = if let Some(modifier) = self.tree_config_modifier {
120 modifier(reth_node_api::TreeConfig::default())
121 } else {
122 reth_node_api::TreeConfig::default()
123 };
124
125 let mut nodes: Vec<NodeTestContext<_, _>> = Vec::with_capacity(self.num_nodes);
126
127 for idx in 0..self.num_nodes {
128 let base_config = NodeConfig::new(self.chain_spec.clone())
130 .with_network(network_config.clone())
131 .with_unused_ports()
132 .with_rpc(
133 RpcServerArgs::default()
134 .with_unused_ports()
135 .with_http()
136 .with_http_api(RpcModuleSelection::All),
137 );
138
139 let node_config = if let Some(modifier) = &self.node_config_modifier {
141 modifier(base_config)
142 } else {
143 base_config
144 };
145
146 let span = span!(Level::INFO, "node", idx);
147 let _enter = span.enter();
148 let node = N::default();
149 let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
150 .testing_node(exec.clone())
151 .with_types_and_provider::<N, BlockchainProvider<_>>()
152 .with_components(node.components_builder())
153 .with_add_ons(node.add_ons())
154 .launch_with_fn(|builder| {
155 let launcher = EngineNodeLauncher::new(
156 builder.task_executor().clone(),
157 builder.config().datadir(),
158 tree_config.clone(),
159 );
160 builder.launch_with(launcher)
161 })
162 .await?;
163
164 let mut node = NodeTestContext::new(node, self.attributes_generator).await?;
165
166 let genesis = node.block_hash(0);
167 node.update_forkchoice(genesis, genesis).await?;
168
169 if self.connect_nodes {
171 if let Some(previous_node) = nodes.last_mut() {
172 previous_node.connect(&mut node).await;
173 }
174
175 if idx + 1 == self.num_nodes &&
177 self.num_nodes > 2 &&
178 let Some(first_node) = nodes.first_mut()
179 {
180 node.connect(first_node).await;
181 }
182 }
183
184 nodes.push(node);
185 }
186
187 Ok((nodes, tasks, Wallet::default().with_chain_id(self.chain_spec.chain().into())))
188 }
189}
190
191impl<N, F> std::fmt::Debug for E2ETestSetupBuilder<N, F>
192where
193 N: NodeBuilderHelper,
194 F: Fn(u64) -> <<N as NodeTypes>::Payload as PayloadTypes>::PayloadBuilderAttributes
195 + Send
196 + Sync
197 + Copy
198 + 'static,
199 LocalPayloadAttributesBuilder<N::ChainSpec>:
200 PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
201{
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 f.debug_struct("E2ETestSetupBuilder")
204 .field("num_nodes", &self.num_nodes)
205 .field("connect_nodes", &self.connect_nodes)
206 .field("tree_config_modifier", &self.tree_config_modifier.as_ref().map(|_| "<closure>"))
207 .field("node_config_modifier", &self.node_config_modifier.as_ref().map(|_| "<closure>"))
208 .finish_non_exhaustive()
209 }
210}