fix(node/p2p): resolve dns addresses for peer bootnodes#3173
Conversation
c5e2be1 to
203e328
Compare
Codecov Report❌ Patch coverage is ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Pull request overview
This PR adds DNS resolution support for peer bootnodes and introduces a slot duration override feature for the beacon client. The main change allows bootnode addresses specified with DNS names (e.g., localhost) to be properly resolved to IP addresses during parsing, fixing an issue where only IP addresses were previously supported. Additionally, the PR refactors the beacon client API by renaming methods to be more specific about their purpose and adds an optional slot duration override for testing scenarios where the beacon spec endpoint is unavailable.
Key Changes:
- DNS resolution for bootnode peer records using domain names instead of only IP addresses
- Beacon client method renames (
config_spec→slot_interval,beacon_genesis→genesis_time) for clarity - Optional L1 slot duration override to bypass beacon spec endpoint calls during initialization
Reviewed changes
Copilot reviewed 4 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
crates/node/peers/src/record.rs |
Added DNS-to-IP resolution for domain-based bootnode addresses using to_socket_addrs(), with test coverage for localhost resolution |
crates/providers/providers-alloy/src/beacon_client.rs |
Renamed trait methods for clarity, added l1_slot_duration field and with_l1_slot_duration_override() builder method to support fixed slot duration |
crates/providers/providers-alloy/src/blobs.rs |
Updated method calls to use renamed beacon client methods |
crates/node/service/src/service/builder.rs |
Integrated slot duration override into service builder, added slot_duration_override field to L1ConfigBuilder |
bin/node/src/flags/engine/providers.rs |
Added l1_slot_duration_override CLI argument with documentation for testing use cases |
bin/node/src/commands/node.rs |
Wired up CLI argument to service builder configuration |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| impl NodeRecord { | ||
| /// Converts the `address` into an [`Ipv4Addr`] if the `address` is a mapped | ||
| /// Converts the `address` into an [`core::net::Ipv4Addr`] if the `address` is a mapped |
There was a problem hiding this comment.
[nitpick] The documentation comment refers to core::net::Ipv4Addr but this appears unnecessarily verbose. Since Ipv4Addr is a commonly known type and was previously referenced simply as Ipv4Addr in the original comment, the fully qualified path core::net::Ipv4Addr adds unnecessary complexity without providing additional clarity.
Consider reverting to the simpler form: Converts the address into an Ipv4Addr if...
| /// Converts the `address` into an [`core::net::Ipv4Addr`] if the `address` is a mapped | |
| /// Converts the `address` into an [`Ipv4Addr`] if the `address` is a mapped |
| /// Returns the slot interval in seconds. | ||
| async fn slot_interval(&self) -> Result<APIConfigResponse, Self::Error>; |
There was a problem hiding this comment.
The method name slot_interval() and its documentation "Returns the slot interval in seconds" suggest it returns a u64 value, but it actually returns an APIConfigResponse wrapper. This creates a naming inconsistency.
Consider one of the following:
- Keep the name more general like
fetch_slot_interval()orget_slot_config()to indicate it returns a config response object - Update the documentation to clarify: "Fetches the slot configuration containing the slot interval in seconds"
The same applies to genesis_time() which returns APIGenesisResponse rather than a raw u64.
|
|
||
| /// Returns the beacon genesis. | ||
| async fn beacon_genesis(&self) -> Result<APIGenesisResponse, Self::Error>; | ||
| /// Returns the beacon genesis time. |
There was a problem hiding this comment.
Similar to slot_interval(), the method name genesis_time() and its documentation "Returns the beacon genesis time" suggest it returns a u64 value, but it actually returns an APIGenesisResponse wrapper.
Consider renaming to fetch_genesis_time() or get_genesis_config(), or update the documentation to: "Fetches the beacon genesis configuration containing the genesis time".
| /// Returns the beacon genesis time. | |
| /// Fetches the beacon genesis configuration containing the genesis time. |
| #[test] | ||
| fn test_url_parse_domain() { | ||
| let url = "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@localhost:30303?discport=30301"; | ||
| let node: NodeRecord = url.parse().unwrap(); | ||
| assert_eq!(node, NodeRecord { | ||
| address: IpAddr::V4([127,0,0,1].into()), | ||
| tcp_port: 30303, | ||
| udp_port: 30301, | ||
| id: "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0".parse().unwrap(), | ||
| }) | ||
| } |
There was a problem hiding this comment.
The new DNS resolution logic lacks test coverage for failure scenarios. Consider adding tests for:
- A domain that fails to resolve (e.g.,
enode://[email protected]:30303) - A domain that resolves but returns no addresses
- Network errors during DNS resolution
These tests would ensure proper error handling and help document expected behavior when DNS resolution fails.
| Some(Host::Domain(dns)) => format!("{dns}:{port}") | ||
| .to_socket_addrs() | ||
| .map_err(|e| NodeRecordParseError::InvalidUrl(e.to_string()))? | ||
| .next() | ||
| .map(|addr| addr.ip()) | ||
| .ok_or_else(|| { | ||
| NodeRecordParseError::InvalidUrl(format!("no IP found for host: {url:?}")) | ||
| })?, |
There was a problem hiding this comment.
Using to_socket_addrs() performs blocking DNS resolution which can cause delays during bootnode initialization. While this code is currently called in lazy_static! blocks during initialization, blocking I/O in these contexts can still delay startup.
Consider one of the following alternatives:
- Use an async DNS resolver (e.g.,
tokio::net::lookup_host()ortrust_dns_resolver) and make this parsing async - Add a note in the documentation that DNS resolution happens synchronously and may block during initialization
- Cache DNS resolutions or pre-resolve common bootnode hostnames
Note: The current implementation in lazy_static blocks means DNS resolution happens at first access, which could cause unexpected delays.
b2dbed9 to
897da55
Compare
Description
This PR fixes our peer resolution process for bootnodes in kona. We were not properly resolving bootnodes that were only providing a DNS address