reth_bench/bench/
context.rs1use crate::{authenticated_transport::AuthenticatedTransportConnect, bench_mode::BenchMode};
5use alloy_eips::BlockNumberOrTag;
6use alloy_primitives::address;
7use alloy_provider::{network::AnyNetwork, Provider, RootProvider};
8use alloy_rpc_client::ClientBuilder;
9use alloy_rpc_types_engine::JwtSecret;
10use alloy_transport::layers::{RateLimitRetryPolicy, RetryBackoffLayer};
11use reqwest::Url;
12use reth_node_core::args::{BenchmarkArgs, WaitForPersistence};
13use tracing::info;
14
15pub(crate) struct BenchContext {
21 pub(crate) auth_provider: RootProvider<AnyNetwork>,
23 pub(crate) block_provider: RootProvider<AnyNetwork>,
25 pub(crate) benchmark_mode: BenchMode,
28 pub(crate) next_block: u64,
30 pub(crate) is_optimism: bool,
32 pub(crate) use_reth_namespace: bool,
34 pub(crate) rlp_blocks: bool,
36 pub(crate) wait_for_persistence: WaitForPersistence,
38 pub(crate) no_wait_for_caches: bool,
40}
41
42impl BenchContext {
43 pub(crate) async fn new(bench_args: &BenchmarkArgs, rpc_url: String) -> eyre::Result<Self> {
46 info!(target: "reth-bench", "Running benchmark using data from RPC URL: {}", rpc_url);
47
48 if let Some(output) = &bench_args.output {
50 if output.is_file() {
51 return Err(eyre::eyre!("Output path must be a directory"));
52 }
53 if !output.exists() {
55 std::fs::create_dir_all(output)?;
56 info!(target: "reth-bench", "Created output directory: {:?}", output);
57 }
58 }
59
60 let retry_policy =
62 RateLimitRetryPolicy::default().or(|err: &alloy_transport::TransportError| -> bool {
63 err.as_transport_err()
64 .and_then(|t| t.as_http_error())
65 .is_some_and(|e| e.status == 502)
66 });
67 let max_retries = bench_args.rpc_block_fetch_retries.as_max_retries();
68 let client = ClientBuilder::default()
69 .layer(RetryBackoffLayer::new_with_policy(max_retries, 800, u64::MAX, retry_policy))
70 .http(rpc_url.parse()?);
71 let block_provider = RootProvider::<AnyNetwork>::new(client);
72
73 let is_optimism = !block_provider
75 .get_code_at(address!("0x420000000000000000000000000000000000000F"))
76 .await?
77 .is_empty();
78
79 let auth_jwt = bench_args
81 .auth_jwtsecret
82 .clone()
83 .ok_or_else(|| eyre::eyre!("--jwt-secret must be provided for authenticated RPC"))?;
84
85 let jwt = std::fs::read_to_string(auth_jwt)?;
89 let jwt = JwtSecret::from_hex(jwt)?;
90
91 let auth_url = Url::parse(&bench_args.engine_rpc_url)?;
93
94 info!(target: "reth-bench", "Connecting to Engine RPC at {} for replay", auth_url);
96 let auth_transport = AuthenticatedTransportConnect::new(auth_url, jwt);
97 let client = ClientBuilder::default().connect_with(auth_transport).await?;
98 let auth_provider = RootProvider::<AnyNetwork>::new(client);
99
100 let (from, to) = if let Some(advance) = bench_args.advance {
109 if advance == 0 {
110 return Err(eyre::eyre!("--advance must be greater than 0"));
111 }
112
113 let head_block = auth_provider
114 .get_block_by_number(BlockNumberOrTag::Latest)
115 .await?
116 .ok_or_else(|| eyre::eyre!("Failed to fetch latest block for --advance"))?;
117 let head_number = head_block.header.number;
118 (Some(head_number), Some(head_number + advance))
119 } else if bench_args.from.is_none() && bench_args.to.is_some() {
120 let head_block = auth_provider
121 .get_block_by_number(BlockNumberOrTag::Latest)
122 .await?
123 .ok_or_else(|| eyre::eyre!("Failed to fetch latest block from engine"))?;
124 let head_number = head_block.header.number;
125 info!(target: "reth-bench", "No --from provided, derived from engine head: {}", head_number);
126 (Some(head_number), bench_args.to)
127 } else {
128 (bench_args.from, bench_args.to)
129 };
130
131 let latest_block = block_provider
134 .get_block_by_number(BlockNumberOrTag::Latest)
135 .full()
136 .await?
137 .ok_or_else(|| eyre::eyre!("Failed to fetch latest block from RPC"))?;
138 let mut benchmark_mode = BenchMode::new(from, to, latest_block.into_inner().number());
139
140 let first_block = match benchmark_mode {
141 BenchMode::Continuous(start) => {
142 block_provider.get_block_by_number(start.into()).full().await?.ok_or_else(|| {
143 eyre::eyre!("Failed to fetch block {} from RPC for continuous mode", start)
144 })?
145 }
146 BenchMode::Range(ref mut range) => {
147 match range.next() {
148 Some(block_number) => {
149 block_provider
151 .get_block_by_number(block_number.into())
152 .full()
153 .await?
154 .ok_or_else(|| {
155 eyre::eyre!("Failed to fetch block {} from RPC", block_number)
156 })?
157 }
158 None => {
159 return Err(eyre::eyre!(
160 "Benchmark mode range is empty, please provide a larger range"
161 ));
162 }
163 }
164 }
165 };
166
167 let next_block = first_block.header.number + 1;
168 let rlp_blocks = bench_args.rlp_blocks;
169 let wait_for_persistence =
170 bench_args.wait_for_persistence.unwrap_or(WaitForPersistence::Never);
171 let use_reth_namespace = bench_args.reth_new_payload || rlp_blocks;
172 let no_wait_for_caches = bench_args.no_wait_for_caches;
173 Ok(Self {
174 auth_provider,
175 block_provider,
176 benchmark_mode,
177 next_block,
178 is_optimism,
179 use_reth_namespace,
180 rlp_blocks,
181 wait_for_persistence,
182 no_wait_for_caches,
183 })
184 }
185}