//! This contains the [`BenchContext`], which is information that all replay-based benchmarks need. //! The initialization code is also the same, so this can be shared across benchmark commands. use crate::{authenticated_transport::AuthenticatedTransportConnect, bench_mode::BenchMode}; use alloy_eips::BlockNumberOrTag; use alloy_provider::{ network::{AnyNetwork, Ethereum}, Provider, ProviderBuilder, RootProvider, }; use alloy_rpc_client::ClientBuilder; use alloy_rpc_types_engine::JwtSecret; use alloy_transport::BoxTransport; use alloy_transport_http::Http; use reqwest::{Client, Url}; use reth_node_core::args::BenchmarkArgs; use tracing::info; /// This is intended to be used by benchmarks that replay blocks from an RPC. /// /// It contains an authenticated provider for engine API queries, a block provider for block /// queries, a [`BenchMode`] to determine whether the benchmark should run for a closed or open /// range of blocks, and the next block to fetch. pub(crate) struct BenchContext { /// The auth provider used for engine API queries. pub(crate) auth_provider: RootProvider, /// The block provider used for block queries. pub(crate) block_provider: RootProvider, Ethereum>, /// The benchmark mode, which defines whether the benchmark should run for a closed or open /// range of blocks. pub(crate) benchmark_mode: BenchMode, /// The next block to fetch. pub(crate) next_block: u64, } impl BenchContext { /// This is the initialization code for most benchmarks, taking in a [`BenchmarkArgs`] and /// returning the providers needed to run a benchmark. pub(crate) async fn new(bench_args: &BenchmarkArgs, rpc_url: String) -> eyre::Result { info!("Running benchmark using data from RPC URL: {}", rpc_url); // Ensure that output directory is a directory if let Some(output) = &bench_args.output { if output.is_file() { return Err(eyre::eyre!("Output path must be a directory")); } } // set up alloy client for blocks let block_provider = ProviderBuilder::new().on_http(rpc_url.parse()?); // If neither `--from` nor `--to` are provided, we will run the benchmark continuously, // starting at the latest block. let mut benchmark_mode = BenchMode::new(bench_args.from, bench_args.to)?; // construct the authenticated provider let auth_jwt = bench_args.auth_jwtsecret.clone().ok_or_else(|| { eyre::eyre!("--auth-jwtsecret must be provided for authenticated RPC") })?; // fetch jwt from file // // the jwt is hex encoded so we will decode it after let jwt = std::fs::read_to_string(auth_jwt)?; let jwt = JwtSecret::from_hex(jwt)?; // get engine url let auth_url = Url::parse(&bench_args.engine_rpc_url)?; // construct the authed transport info!("Connecting to Engine RPC at {} for replay", auth_url); let auth_transport = AuthenticatedTransportConnect::new(auth_url, jwt); let client = ClientBuilder::default().connect_boxed(auth_transport).await?; let auth_provider = RootProvider::<_, AnyNetwork>::new(client); let first_block = match benchmark_mode { BenchMode::Continuous => { // fetch Latest block block_provider.get_block_by_number(BlockNumberOrTag::Latest, true).await?.unwrap() } BenchMode::Range(ref mut range) => { match range.next() { Some(block_number) => { // fetch first block in range block_provider .get_block_by_number(block_number.into(), true) .await? .unwrap() } None => { return Err(eyre::eyre!( "Benchmark mode range is empty, please provide a larger range" )); } } } }; let next_block = match first_block.header.number { Some(number) => { // fetch next block number + 1 } None => { // this should never happen return Err(eyre::eyre!("First block number is None")); } }; Ok(Self { auth_provider, block_provider, benchmark_mode, next_block }) } }