Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(anvil): allow pass headers to fork-url #6178

Merged
merged 6 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 35 additions & 6 deletions crates/anvil/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ impl NodeArgs {
.fork_block_number
.or_else(|| self.evm_opts.fork_url.as_ref().and_then(|f| f.block)),
)
.with_fork_headers(self.evm_opts.fork_headers)
.with_fork_chain_id(self.evm_opts.fork_chain_id.map(u64::from))
.fork_request_timeout(self.evm_opts.fork_request_timeout.map(Duration::from_millis))
.fork_request_retries(self.evm_opts.fork_request_retries)
Expand Down Expand Up @@ -328,6 +329,17 @@ pub struct AnvilEvmArgs {
)]
pub fork_url: Option<ForkUrl>,

/// Pass headers to fork-url.
///
/// See --fork-url.
#[clap(
long = "fork-header",
value_name = "HEADERS",
help_heading = "Fork config",
requires = "fork_url"
)]
pub fork_headers: Vec<String>,

/// Timeout in ms for requests sent to remote JSON-RPC server in forking mode.
///
/// Default value 45000
Expand Down Expand Up @@ -527,7 +539,7 @@ impl Future for PeriodicStateDumper {
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
if this.dump_state.is_none() {
return Poll::Pending
return Poll::Pending;
}

loop {
Expand All @@ -538,7 +550,7 @@ impl Future for PeriodicStateDumper {
}
Poll::Pending => {
this.in_progress_dump = Some(flush);
return Poll::Pending
return Poll::Pending;
}
}
}
Expand All @@ -548,7 +560,7 @@ impl Future for PeriodicStateDumper {
let path = this.dump_state.clone().expect("exists; see above");
this.in_progress_dump = Some(Box::pin(PeriodicStateDumper::dump_state(api, path)));
} else {
break
break;
}
}

Expand All @@ -573,7 +585,7 @@ impl StateFile {
}
let mut state = Self { path, state: None };
if !state.path.exists() {
return Ok(state)
return Ok(state);
}

state.state = Some(SerializableState::load(&state.path).map_err(|err| err.to_string())?);
Expand Down Expand Up @@ -608,14 +620,14 @@ impl FromStr for ForkUrl {
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some((url, block)) = s.rsplit_once('@') {
if block == "latest" {
return Ok(ForkUrl { url: url.to_string(), block: None })
return Ok(ForkUrl { url: url.to_string(), block: None });
}
// this will prevent false positives for auths `user:password@example.com`
if !block.is_empty() && !block.contains(':') && !block.contains('.') {
let block: u64 = block
.parse()
.map_err(|_| format!("Failed to parse block number: `{block}`"))?;
return Ok(ForkUrl { url: url.to_string(), block: Some(block) })
return Ok(ForkUrl { url: url.to_string(), block: Some(block) });
}
}
Ok(ForkUrl { url: s.to_string(), block: None })
Expand Down Expand Up @@ -664,6 +676,23 @@ mod tests {
assert_eq!(args.hardfork, Some(Hardfork::Berlin));
}

#[test]
fn can_parse_fork_headers() {
let args: NodeArgs = NodeArgs::parse_from([
"anvil",
"--fork-url",
"http,://localhost:8545",
"--fork-header",
"User-Agent: test-agent",
"--fork-header",
"Referrer: example.com",
]);
assert_eq!(
args.evm_opts.fork_headers,
vec!["User-Agent: test-agent", "Referrer: example.com"]
);
}

#[test]
fn can_parse_prune_config() {
let args: NodeArgs = NodeArgs::parse_from(["anvil", "--prune-history"]);
Expand Down
17 changes: 14 additions & 3 deletions crates/anvil/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ pub struct NodeConfig {
pub eth_rpc_url: Option<String>,
/// pins the block number for the state fork
pub fork_block_number: Option<u64>,
/// headers to use with `eth_rpc_url`
pub fork_headers: Vec<String>,
/// specifies chain id for cache to skip fetching from remote in offline-start mode
pub fork_chain_id: Option<U256>,
/// The generator used to generate the dev accounts
Expand Down Expand Up @@ -392,6 +394,7 @@ impl Default for NodeConfig {
config_out: None,
genesis: None,
fork_request_timeout: REQUEST_TIMEOUT,
fork_headers: vec![],
fork_request_retries: 5,
fork_retry_backoff: Duration::from_millis(1_000),
fork_chain_id: None,
Expand Down Expand Up @@ -659,6 +662,13 @@ impl NodeConfig {
self
}

/// Sets the `fork_headers` to use with `eth_rpc_url`
#[must_use]
pub fn with_fork_headers(mut self, headers: Vec<String>) -> Self {
self.fork_headers = headers;
self
}

/// Sets the `fork_request_timeout` to use for requests
#[must_use]
pub fn fork_request_timeout(mut self, fork_request_timeout: Option<Duration>) -> Self {
Expand Down Expand Up @@ -756,7 +766,7 @@ impl NodeConfig {
.expect("Failed writing json");
}
if self.silent {
return
return;
}

println!("{}", self.as_string(fork))
Expand All @@ -767,7 +777,7 @@ impl NodeConfig {
/// See also [ Config::foundry_block_cache_file()]
pub fn block_cache_path(&self, block: u64) -> Option<PathBuf> {
if self.no_storage_caching || self.eth_rpc_url.is_none() {
return None
return None;
}
let chain_id = self.get_chain_id();

Expand Down Expand Up @@ -901,6 +911,7 @@ impl NodeConfig {
.compute_units_per_second(self.compute_units_per_second)
.max_retry(10)
.initial_backoff(1000)
.headers(self.fork_headers.clone())
.build()
.expect("Failed to establish provider to fork url"),
);
Expand Down Expand Up @@ -1178,7 +1189,7 @@ async fn find_latest_fork_block<M: Middleware>(provider: M) -> Result<u64, M::Er
for _ in 0..2 {
if let Some(block) = provider.get_block(num).await? {
if block.hash.is_some() {
break
break;
}
}
// block not actually finalized, so we try the block before
Expand Down
31 changes: 25 additions & 6 deletions crates/common/src/provider.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Commonly used helpers to construct `Provider`s

use crate::{runtime_client::RuntimeClient, ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT};
use crate::{
runtime_client::{RuntimeClient, RuntimeClientBuilder},
ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT,
};
use ethers_core::types::{Chain, U256};
use ethers_middleware::gas_oracle::{GasCategory, GasOracle, Polygon};
use ethers_providers::{is_local_endpoint, Middleware, Provider, DEFAULT_LOCAL_POLL_INTERVAL};
Expand Down Expand Up @@ -58,6 +61,7 @@ pub struct ProviderBuilder {
compute_units_per_second: u64,
/// JWT Secret
jwt: Option<String>,
headers: Vec<String>,
}

// === impl ProviderBuilder ===
Expand All @@ -69,7 +73,7 @@ impl ProviderBuilder {
if url_str.starts_with("localhost:") {
// invalid url: non-prefixed URL scheme is not allowed, so we prepend the default http
// prefix
return Self::new(format!("http://{url_str}"))
return Self::new(format!("http://{url_str}"));
}

let url = Url::parse(url_str)
Expand Down Expand Up @@ -103,6 +107,7 @@ impl ProviderBuilder {
// alchemy max cpus <https://github.com/alchemyplatform/alchemy-docs/blob/master/documentation/compute-units.md#rate-limits-cups>
compute_units_per_second: ALCHEMY_FREE_TIER_CUPS,
jwt: None,
headers: vec![],
}
}

Expand Down Expand Up @@ -174,6 +179,13 @@ impl ProviderBuilder {
self
}

/// Sets http headers
pub fn headers(mut self, headers: Vec<String>) -> Self {
self.headers = headers;

self
}

/// Same as [`Self:build()`] but also retrieves the `chainId` in order to derive an appropriate
/// interval
pub async fn connect(self) -> eyre::Result<RetryProvider> {
Expand All @@ -197,18 +209,25 @@ impl ProviderBuilder {
timeout,
compute_units_per_second,
jwt,
headers,
} = self;
let url = url?;

let mut provider = Provider::new(RuntimeClient::new(
let mut client_builder = RuntimeClientBuilder::new(
url.clone(),
max_retry,
timeout_retry,
initial_backoff,
timeout,
compute_units_per_second,
jwt,
));
)
.with_headers(headers);

if let Some(jwt) = jwt {
client_builder = client_builder.with_jwt(jwt);
}

let mut provider = Provider::new(client_builder.build());

let is_local = is_local_endpoint(url.as_str());

Expand Down Expand Up @@ -269,7 +288,7 @@ where
match chain {
Chain::Polygon | Chain::PolygonMumbai => {
let estimator = Polygon::new(chain)?.category(GasCategory::Standard);
return Ok(estimator.estimate_eip1559_fees().await?)
return Ok(estimator.estimate_eip1559_fees().await?);
}
_ => {}
}
Expand Down
Loading