Skip to content

Commit

Permalink
Add retries to DNS lookup for enode hostnames
Browse files Browse the repository at this point in the history
  • Loading branch information
sergerad committed May 23, 2024
1 parent cdfdaff commit 9485ba5
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 11 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ quote = "1.0"
tokio-stream = "0.1.11"
tokio = { version = "1.21", default-features = false }
tokio-util = { version = "0.7.4", features = ["codec"] }
tokio-retry = { version = "0.3.0" }

# async
async-stream = "0.3"
Expand Down
2 changes: 1 addition & 1 deletion bin/reth/src/commands/stage/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ impl Command {
config.peers.trusted_nodes_only = self.network.trusted_only;
if !self.network.trusted_peers.is_empty() {
for peer in &self.network.trusted_peers {
let peer = resolve_dns_node_record(peer.clone()).await?;
let peer = resolve_dns_node_record(peer.clone(), None).await?;
config.peers.trusted_nodes.insert(peer);
}
}
Expand Down
4 changes: 4 additions & 0 deletions crates/net/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ workspace = true
# ethereum
alloy-primitives.workspace = true

# eth
alloy-primitives = { workspace = true, features = ["rand"] }

# async
pin-project.workspace = true
url.workspace = true
tokio = { workspace = true, features = ["full"] }
tokio-retry = { workspace = true }
85 changes: 77 additions & 8 deletions crates/net/common/src/dns_node_record_resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,26 @@ use reth_network_types::{DNSNodeRecord, NodeRecord};
use std::io::Error;
use url::Host;

/// Retry strategy for DNS lookups.
#[derive(Debug)]
pub struct RetryStrategy {
/// The amount of time between retries.
interval: u64,
/// The number of retries.
retries: usize,
}

/// Resolves the host in a [DNSNodeRecord] to an IP address, returning a [NodeRecord]. If the domain
/// cannot be resolved, an error is returned.
///
/// If the host is already an IP address, the [NodeRecord] is returned immediately.
pub async fn resolve_dns_node_record(node_record: DNSNodeRecord) -> Result<NodeRecord, Error> {
///
/// An optional retry strategy can be provided via [RetryStrategy] to retry
/// the DNS lookup if it fails:
pub async fn resolve_dns_node_record(
node_record: DNSNodeRecord,
retry_strategy: Option<RetryStrategy>,
) -> Result<NodeRecord, Error> {
let domain = match node_record.host {
Host::Ipv4(ip) => {
let id = node_record.id;
Expand All @@ -27,15 +42,69 @@ pub async fn resolve_dns_node_record(node_record: DNSNodeRecord) -> Result<NodeR
Host::Domain(domain) => domain,
};

let mut ips = tokio::net::lookup_host(domain).await?;
// Lookip ipaddr from domain
match lookup_host(&domain).await {
Ok(ip) => Ok(NodeRecord {
address: ip,
id: node_record.id,
tcp_port: node_record.tcp_port,
udp_port: node_record.udp_port,
}),
Err(e) => {
// Retry if strategy is provided
if let Some(strat) = retry_strategy {
let lookup = || async { lookup_host(&domain).await };
let strat = tokio_retry::strategy::FixedInterval::from_millis(strat.interval)
.take(strat.retries);
let ip = tokio_retry::Retry::spawn(strat, lookup).await?;
Ok(NodeRecord {
address: ip,
id: node_record.id,
tcp_port: node_record.tcp_port,
udp_port: node_record.udp_port,
})
} else {
Err(e)
}
}
}
}

async fn lookup_host(domain: &str) -> Result<std::net::IpAddr, Error> {
let mut ips = tokio::net::lookup_host(format!("{domain}:0")).await?;
let ip = ips
.next()
.ok_or_else(|| Error::new(std::io::ErrorKind::AddrNotAvailable, "No IP found"))?;
Ok(ip.ip())
}

mod tests {
#[tokio::test]
async fn test_resolve_dns_node_record() {
use super::*;

// Set up tests
let tests = vec![
("localhost", None),
("localhost", Some(RetryStrategy { interval: 1000, retries: 3 })),
];

Ok(NodeRecord {
address: ip.ip(),
id: node_record.id,
tcp_port: node_record.tcp_port,
udp_port: node_record.udp_port,
})
// Run tests
for (domain, retry_strategy) in tests {
// Construct record
let rec = DNSNodeRecord::new(
url::Host::Domain(domain.to_owned()),
30300,
reth_network_types::PeerId::random(),
);

// Resolve domain and validate
let rec = super::resolve_dns_node_record(rec.clone(), retry_strategy).await.unwrap();
if let std::net::IpAddr::V4(addr) = rec.address {
assert_eq!(addr, std::net::Ipv4Addr::new(127, 0, 0, 1))
} else {
panic!("Expected IPv4 address");
}
}
}
}
3 changes: 3 additions & 0 deletions crates/net/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

// PeerId::random() trait
use alloy_primitives as _;

pub mod ban_list;
pub mod bandwidth_meter;

Expand Down
2 changes: 1 addition & 1 deletion crates/net/network/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ where
// resolve boot nodes
let mut resolved_boot_nodes = vec![];
for record in boot_nodes.iter() {
let resolved = resolve_dns_node_record(record.clone()).await?;
let resolved = resolve_dns_node_record(record.clone(), None).await?;
resolved_boot_nodes.push(resolved);
}

Expand Down
2 changes: 1 addition & 1 deletion crates/node/builder/src/launch/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl LaunchContext {

// resolve trusted peers if they use a domain instead of dns
for peer in &config.network.trusted_peers {
let resolved = resolve_dns_node_record(peer.clone())
let resolved = resolve_dns_node_record(peer.clone(), None)
.await
.wrap_err_with(|| format!("Could not resolve trusted peer {peer}"))?;
toml_config.peers.trusted_nodes.insert(resolved);
Expand Down

0 comments on commit 9485ba5

Please sign in to comment.