Summary
The --arc.tx.relays configuration currently accepts any URL that can be parsed by url::Url, including schemes that cannot be used by the transaction relay's HTTP transport.
For example, the following value is accepted during startup validation:
file:///tmp/upstream
The value is later passed to an explicitly HTTP-based RPC client. This allows the node to start with an unusable relay configuration instead of rejecting it immediately with a clear configuration error.
Affected Files
crates/node/src/main.rs
crates/evm-node/src/rpc_middleware.rs
The validation gap is in build_tx_relays() in crates/node/src/main.rs.
Observed Behavior
build_tx_relays() currently validates each configured relay by calling:
rust
url::Url::parse(s)
This only establishes that the input is a syntactically valid URL. It does not verify that its scheme is supported by the downstream transport.
As a result, this configuration is accepted:
--arc.tx.relays file:///tmp/upstream
The returned relay list contains:
["file:///tmp/upstream"]
However, the relay implementation later constructs each client with:
rust
RpcClient::new_http_with_client(...)
using a reqwest::Client. Therefore, a file:// URL cannot function as a transaction relay upstream.
The invalid configuration is detected only when forwarding is attempted, where it becomes a transport/failover error rather than a startup validation error.
Expected Behavior
--arc.tx.relays should only accept URL schemes supported by the transaction relay transport:
http
https
Unsupported schemes such as the following should be rejected during startup:
file://
ftp://
ws://
wss://
The error should identify the invalid --arc.tx.relays entry and explain that an HTTP or HTTPS URL is required.
Reproduction
I added the following focused regression test against current main:
rust
#[test]
fn test_build_tx_relays_rejects_non_http_url() {
let err = tx_relays_from_args(&[
"--arc.tx.relays",
"file:///tmp/upstream",
])
.expect_err("transaction relay upstreams must use HTTP or HTTPS");
assert!(
err.to_string()
.contains("invalid --arc.tx.relays entry")
);
}
The test fails because the current implementation returns the URL as a valid relay configuration:
thread 'tests::test_build_tx_relays_rejects_non_http_url' panicked:
transaction relay upstreams must use HTTP or HTTPS:
["file:///tmp/upstream"]
test tests::test_build_tx_relays_rejects_non_http_url ... FAILED
The test was executed against:
97f8da0
which was the current main SHA at the time of verification.
Root Cause
The validation boundary checks URL syntax but not transport compatibility:
rust
url::Url::parse(s)
.map(|_| s.to_string())
The downstream implementation assumes an HTTP transport:
rust
RpcClient::new_http_with_client(http.clone(), url)
Therefore, the validation contract is weaker than the runtime transport contract.
Why This Matters
Invalid relay configuration should fail before the node starts.
Accepting unsupported schemes can cause:
- a node to appear correctly configured at startup
- transaction forwarding to fail only when it is first needed
- unnecessary failover attempts
- misleading relay exhaustion or generic transport errors
- longer diagnosis time for an operator configuration error
This is especially relevant because --arc.tx.relays is a prioritized list: one unsupported entry can introduce avoidable failures before a working upstream is selected.
Suggested Fix
After parsing each URL, validate its scheme:
rust
let url = url::Url::parse(s)
.map_err(|e| eyre::eyre!(
"invalid --arc.tx.relays entry {s:?}: {e}"
))?;
if !matches!(url.scheme(), "http" | "https") {
return Err(eyre::eyre!(
"invalid --arc.tx.relays entry {s:?}:
transaction relay upstreams must use HTTP or HTTPS"
));
}
The existing ordering and trimmed string behavior can otherwise remain unchanged.
Potential Regression Tests
Add focused tests verifying that:
- http://a:8545 is accepted
- https://a.example is accepted
- file:///tmp/upstream is rejected
- ftp://a.example is rejected
- malformed URLs remain rejected
- multiple HTTP/HTTPS relay URLs preserve their configured order
Scope
This should be a small fail-fast configuration fix.
It does not require changing:
- transaction forwarding behavior
- failover ordering
- request timeout handling
- the RPC client implementation
- protocol or consensus behavior
Duplicate Check
I searched open and closed issues and pull requests using:
- arc.tx.relays
- transaction relay URL scheme
- TxRelays HTTP upstream
- file:// relay
- new_http_with_client
I did not find a direct duplicate or an existing PR implementing this validation.
Issue #59 concerns the older singular --rpc.forwarder path and does not cover --arc.tx.relays scheme validation.
Summary
The
--arc.tx.relaysconfiguration currently accepts any URL that can be parsed byurl::Url, including schemes that cannot be used by the transaction relay's HTTP transport.For example, the following value is accepted during startup validation:
file:///tmp/upstream
The value is later passed to an explicitly HTTP-based RPC client. This allows the node to start with an unusable relay configuration instead of rejecting it immediately with a clear configuration error.
Affected Files
crates/node/src/main.rs
crates/evm-node/src/rpc_middleware.rs
The validation gap is in
build_tx_relays()incrates/node/src/main.rs.Observed Behavior
build_tx_relays()currently validates each configured relay by calling:rust
url::Url::parse(s)
This only establishes that the input is a syntactically valid URL. It does not verify that its scheme is supported by the downstream transport.
As a result, this configuration is accepted:
--arc.tx.relays file:///tmp/upstream
The returned relay list contains:
["file:///tmp/upstream"]
However, the relay implementation later constructs each client with:
rust
RpcClient::new_http_with_client(...)
using a
reqwest::Client. Therefore, afile://URL cannot function as a transaction relay upstream.The invalid configuration is detected only when forwarding is attempted, where it becomes a transport/failover error rather than a startup validation error.
Expected Behavior
--arc.tx.relaysshould only accept URL schemes supported by the transaction relay transport:http
https
Unsupported schemes such as the following should be rejected during startup:
file://
ftp://
ws://
wss://
The error should identify the invalid
--arc.tx.relaysentry and explain that an HTTP or HTTPS URL is required.Reproduction
I added the following focused regression test against current main:
rust
#[test]
fn test_build_tx_relays_rejects_non_http_url() {
let err = tx_relays_from_args(&[
"--arc.tx.relays",
"file:///tmp/upstream",
])
.expect_err("transaction relay upstreams must use HTTP or HTTPS");
}
The test fails because the current implementation returns the URL as a valid relay configuration:
thread 'tests::test_build_tx_relays_rejects_non_http_url' panicked:
transaction relay upstreams must use HTTP or HTTPS:
["file:///tmp/upstream"]
test tests::test_build_tx_relays_rejects_non_http_url ... FAILED
The test was executed against:
97f8da0
which was the current main SHA at the time of verification.
Root Cause
The validation boundary checks URL syntax but not transport compatibility:
rust
url::Url::parse(s)
.map(|_| s.to_string())
The downstream implementation assumes an HTTP transport:
rust
RpcClient::new_http_with_client(http.clone(), url)
Therefore, the validation contract is weaker than the runtime transport contract.
Why This Matters
Invalid relay configuration should fail before the node starts.
Accepting unsupported schemes can cause:
This is especially relevant because
--arc.tx.relaysis a prioritized list: one unsupported entry can introduce avoidable failures before a working upstream is selected.Suggested Fix
After parsing each URL, validate its scheme:
rust
let url = url::Url::parse(s)
.map_err(|e| eyre::eyre!(
"invalid --arc.tx.relays entry {s:?}: {e}"
))?;
if !matches!(url.scheme(), "http" | "https") {
return Err(eyre::eyre!(
"invalid --arc.tx.relays entry {s:?}:
transaction relay upstreams must use HTTP or HTTPS"
));
}
The existing ordering and trimmed string behavior can otherwise remain unchanged.
Potential Regression Tests
Add focused tests verifying that:
Scope
This should be a small fail-fast configuration fix.
It does not require changing:
Duplicate Check
I searched open and closed issues and pull requests using:
I did not find a direct duplicate or an existing PR implementing this validation.
Issue #59 concerns the older singular
--rpc.forwarderpath and does not cover--arc.tx.relaysscheme validation.