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(discv5): open dns for discv5 #7328

Merged
merged 18 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 16 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
1 change: 1 addition & 0 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion crates/net/dns/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ pub struct DnsNodeRecordUpdate {
pub node_record: NodeRecord,
/// The forkid of the node, if present in the ENR
pub fork_id: Option<ForkId>,
/// Original [`Enr`].
pub enr: Enr<SecretKey>,
}

/// Commands sent from [DnsDiscoveryHandle] to [DnsDiscoveryService]
Expand Down Expand Up @@ -403,7 +405,7 @@ fn convert_enr_node_record(enr: &Enr<SecretKey>) -> Option<DnsNodeRecordUpdate>
let mut maybe_fork_id = enr.get(b"eth")?;
let fork_id = ForkId::decode(&mut maybe_fork_id).ok();

Some(DnsNodeRecordUpdate { node_record, fork_id })
Some(DnsNodeRecordUpdate { node_record, fork_id, enr: enr.clone() })
}

#[cfg(test)]
Expand Down
5 changes: 3 additions & 2 deletions crates/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ pub use header::{Header, HeaderValidationError, HeadersDirection, SealedHeader};
pub use integer_list::IntegerList;
pub use log::{logs_bloom, Log};
pub use net::{
goerli_nodes, holesky_nodes, mainnet_nodes, parse_nodes, sepolia_nodes, NodeRecord,
GOERLI_BOOTNODES, HOLESKY_BOOTNODES, MAINNET_BOOTNODES, SEPOLIA_BOOTNODES,
get_fork_id, goerli_nodes, holesky_nodes, mainnet_nodes, parse_nodes, pk_to_id, sepolia_nodes,
NetworkIdError, NodeRecord, NodeRecordParseError, GOERLI_BOOTNODES, HOLESKY_BOOTNODES,
MAINNET_BOOTNODES, SEPOLIA_BOOTNODES,
};
pub use peer::{AnyNode, PeerId, WithPeerId};
pub use prune::{
Expand Down
71 changes: 69 additions & 2 deletions crates/primitives/src/net.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
pub use reth_rpc_types::NodeRecord;
use alloy_rlp::Decodable;
use enr::Enr;
pub use reth_rpc_types::{pk_to_id, NodeRecord, NodeRecordParseError};
use thiserror::Error;

use crate::ForkId;
emhane marked this conversation as resolved.
Show resolved Hide resolved

// <https://github.com/ledgerwatch/erigon/blob/610e648dc43ec8cd6563313e28f06f534a9091b3/params/bootnodes.go>

Expand Down Expand Up @@ -66,17 +71,46 @@ pub fn parse_nodes(nodes: impl IntoIterator<Item = impl AsRef<str>>) -> Vec<Node
nodes.into_iter().map(|s| s.as_ref().parse().unwrap()).collect()
}

/// Error thrown when network can't be identified on a node record.
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum NetworkIdError {
/// Missing key used to identify an execution layer enr on Ethereum network.
#[error("fork id missing on enr, 'eth' key missing")]
EthForkIdMissing,
/// Failed to decode fork ID rlp value.
#[error("failed to decode fork id, 'eth': {0:?}")]
ForkIdDecodeError(Vec<u8>),
}

/// Tries to read the [`ForkId`] from given [`Enr`].
pub fn get_fork_id(enr: &Enr<secp256k1::SecretKey>) -> Result<ForkId, NetworkIdError> {
let Some(mut maybe_fork_id) = enr.get(b"eth") else {
return Err(NetworkIdError::EthForkIdMissing)
};

let Ok(fork_id) = ForkId::decode(&mut maybe_fork_id) else {
return Err(NetworkIdError::ForkIdDecodeError(maybe_fork_id.to_vec()))
};

Ok(fork_id)
}

emhane marked this conversation as resolved.
Show resolved Hide resolved
#[cfg(test)]
mod tests {
use std::{
net::{IpAddr, Ipv4Addr},
str::FromStr,
};

use crate::MAINNET;

use super::*;
use alloy_rlp::Decodable;
use alloy_rlp::Encodable;
use rand::{thread_rng, Rng, RngCore};
use reth_ethereum_forks::Hardfork;
use reth_rpc_types::PeerId;
use secp256k1::SecretKey;

#[test]
fn test_mapped_ipv6() {
Expand Down Expand Up @@ -197,4 +231,37 @@ mod tests {
id: PeerId::from_str("6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0").unwrap(),
})
}

#[test]
fn conversion_to_node_record_from_enr() {
const IP: &str = "::";
const TCP_PORT: u16 = 30303;
const UDP_PORT: u16 = 9000;

let mut rng = thread_rng();
let key = SecretKey::new(&mut rng);

let mut buf = Vec::new();
let fork_id = MAINNET.hardfork_fork_id(Hardfork::Frontier);
fork_id.unwrap().encode(&mut buf);

let enr = Enr::builder()
.ip6(IP.parse().unwrap())
.udp6(UDP_PORT)
.tcp6(TCP_PORT)
.build(&key)
.unwrap();

let node_record = NodeRecord::try_from(&enr).unwrap();

assert_eq!(
NodeRecord {
address: IP.parse().unwrap(),
tcp_port: TCP_PORT,
udp_port: UDP_PORT,
id: pk_to_id(&enr.public_key())
},
node_record
)
}
}
1 change: 1 addition & 0 deletions crates/rpc/rpc-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ alloy-rpc-engine-types = { workspace = true, features = ["jsonrpsee-types"] }
ethereum_ssz_derive = { version = "0.5", optional = true }
ethereum_ssz = { version = "0.5", optional = true }
alloy-genesis.workspace = true
enr = { workspace = true, features = ["serde", "rust-secp256k1"] }

# misc
thiserror.workspace = true
Expand Down
31 changes: 28 additions & 3 deletions crates/rpc/rpc-types/src/net.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::PeerId;
use crate::{pk_to_id, PeerId};
use alloy_rlp::{RlpDecodable, RlpEncodable};
use enr::Enr;
use secp256k1::{SecretKey, SECP256K1};
use serde_with::{DeserializeFromStr, SerializeDisplay};
use std::{
Expand All @@ -9,6 +10,7 @@ use std::{
num::ParseIntError,
str::FromStr,
};
use thiserror::Error;
use url::{Host, Url};

/// Represents a ENR in discovery.
Expand Down Expand Up @@ -114,8 +116,8 @@ impl fmt::Display for NodeRecord {
}
}

/// Possible error types when parsing a `NodeRecord`
#[derive(Debug, thiserror::Error)]
/// Possible error types when parsing a [`NodeRecord`]
#[derive(Debug, Error)]
pub enum NodeRecordParseError {
/// Invalid url
#[error("Failed to parse url: {0}")]
Expand Down Expand Up @@ -165,6 +167,29 @@ impl FromStr for NodeRecord {
}
}

impl TryFrom<&Enr<SecretKey>> for NodeRecord {
type Error = NodeRecordParseError;

fn try_from(enr: &Enr<SecretKey>) -> Result<Self, Self::Error> {
let Some(address) = enr.ip4().map(IpAddr::from).or_else(|| enr.ip6().map(IpAddr::from))
else {
return Err(NodeRecordParseError::InvalidUrl("ip missing".to_string()))
};

let Some(udp_port) = enr.udp4().or_else(|| enr.udp6()) else {
return Err(NodeRecordParseError::InvalidUrl("udp port missing".to_string()))
};

let Some(tcp_port) = enr.tcp4().or_else(|| enr.tcp6()) else {
return Err(NodeRecordParseError::InvalidUrl("tcp port missing".to_string()))
};

let id = pk_to_id(&enr.public_key());

Ok(NodeRecord { address, tcp_port, udp_port, id }.into_ipv4_mapped())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
5 changes: 5 additions & 0 deletions crates/rpc/rpc-types/src/peer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ use alloy_primitives::B512;

/// Alias for a peer identifier
pub type PeerId = B512;

/// Converts a [`secp256k1::PublicKey`] to a [`PeerId`].
pub fn pk_to_id(pk: &secp256k1::PublicKey) -> PeerId {
PeerId::from_slice(&pk.serialize_uncompressed()[1..])
}
Loading