mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
chore(discv5): pub methods (#8057)
This commit is contained in:
@ -143,7 +143,7 @@ impl ConfigBuilder {
|
||||
}
|
||||
|
||||
/// Sets the tcp port to advertise in the local [`Enr`](discv5::enr::Enr).
|
||||
fn tcp_port(mut self, port: u16) -> Self {
|
||||
pub fn tcp_port(mut self, port: u16) -> Self {
|
||||
self.tcp_port = port;
|
||||
self
|
||||
}
|
||||
|
||||
@ -35,14 +35,12 @@ impl MustIncludeKey {
|
||||
/// Returns [`FilterOutcome::Ok`] if [`Enr`](discv5::Enr) contains the configured kv-pair key.
|
||||
pub fn filter(&self, enr: &discv5::Enr) -> FilterOutcome {
|
||||
if enr.get_raw_rlp(self.key).is_none() {
|
||||
return FilterOutcome::Ignore { reason: self.ignore_reason() }
|
||||
return FilterOutcome::Ignore {
|
||||
reason: format!("{} fork required", String::from_utf8_lossy(self.key)),
|
||||
}
|
||||
}
|
||||
FilterOutcome::Ok
|
||||
}
|
||||
|
||||
fn ignore_reason(&self) -> String {
|
||||
format!("{} fork required", String::from_utf8_lossy(self.key))
|
||||
}
|
||||
}
|
||||
|
||||
/// Filter requiring that peers not advertise kv-pairs using certain keys, e.g. b"eth2".
|
||||
@ -69,20 +67,18 @@ impl MustNotIncludeKeys {
|
||||
pub fn filter(&self, enr: &discv5::Enr) -> FilterOutcome {
|
||||
for key in self.keys.iter() {
|
||||
if matches!(key.filter(enr), FilterOutcome::Ok) {
|
||||
return FilterOutcome::Ignore { reason: self.ignore_reason() }
|
||||
return FilterOutcome::Ignore {
|
||||
reason: format!(
|
||||
"{} forks not allowed",
|
||||
self.keys.iter().map(|key| String::from_utf8_lossy(key.key)).format(",")
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilterOutcome::Ok
|
||||
}
|
||||
|
||||
fn ignore_reason(&self) -> String {
|
||||
format!(
|
||||
"{} forks not allowed",
|
||||
self.keys.iter().map(|key| String::from_utf8_lossy(key.key)).format(",")
|
||||
)
|
||||
}
|
||||
|
||||
/// Adds a key that must not be present for any kv-pair in a node record.
|
||||
pub fn add_disallowed_keys(&mut self, keys: &[&'static [u8]]) {
|
||||
for key in keys {
|
||||
|
||||
@ -161,7 +161,7 @@ impl Discv5 {
|
||||
//
|
||||
// 1. make local enr from listen config
|
||||
//
|
||||
let (enr, bc_enr, fork_key, ip_mode) = Self::build_local_enr(sk, &discv5_config);
|
||||
let (enr, bc_enr, fork_key, ip_mode) = build_local_enr(sk, &discv5_config);
|
||||
|
||||
trace!(target: "net::discv5",
|
||||
?enr,
|
||||
@ -197,14 +197,14 @@ impl Discv5 {
|
||||
//
|
||||
// 3. add boot nodes
|
||||
//
|
||||
Self::bootstrap(bootstrap_nodes, &discv5).await?;
|
||||
bootstrap(bootstrap_nodes, &discv5).await?;
|
||||
|
||||
let metrics = Discv5Metrics::default();
|
||||
|
||||
//
|
||||
// 4. start bg kbuckets maintenance
|
||||
//
|
||||
Self::spawn_populate_kbuckets_bg(
|
||||
spawn_populate_kbuckets_bg(
|
||||
lookup_interval,
|
||||
bootstrap_lookup_interval,
|
||||
bootstrap_lookup_countdown,
|
||||
@ -219,7 +219,177 @@ impl Discv5 {
|
||||
))
|
||||
}
|
||||
|
||||
fn build_local_enr(
|
||||
/// Process an event from the underlying [`discv5::Discv5`] node.
|
||||
pub fn on_discv5_update(&mut self, update: discv5::Event) -> Option<DiscoveredPeer> {
|
||||
match update {
|
||||
discv5::Event::SocketUpdated(_) | discv5::Event::TalkRequest(_) |
|
||||
// `Discovered` not unique discovered peers
|
||||
discv5::Event::Discovered(_) => None,
|
||||
discv5::Event::NodeInserted { replaced: _, .. } => {
|
||||
|
||||
// node has been inserted into kbuckets
|
||||
|
||||
// `replaced` partly covers `reth_discv4::DiscoveryUpdate::Removed(_)`
|
||||
|
||||
self.metrics.discovered_peers.increment_kbucket_insertions(1);
|
||||
|
||||
None
|
||||
}
|
||||
discv5::Event::SessionEstablished(enr, remote_socket) => {
|
||||
// covers `reth_discv4::DiscoveryUpdate` equivalents `DiscoveryUpdate::Added(_)`
|
||||
// and `DiscoveryUpdate::DiscoveredAtCapacity(_)
|
||||
|
||||
// peer has been discovered as part of query, or, by incoming session (peer has
|
||||
// discovered us)
|
||||
|
||||
self.metrics.discovered_peers_advertised_networks.increment_once_by_network_type(&enr);
|
||||
|
||||
self.metrics.discovered_peers.increment_established_sessions_raw(1);
|
||||
|
||||
self.on_discovered_peer(&enr, remote_socket)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes a discovered peer. Returns `true` if peer is added to
|
||||
pub fn on_discovered_peer(
|
||||
&mut self,
|
||||
enr: &discv5::Enr,
|
||||
socket: SocketAddr,
|
||||
) -> Option<DiscoveredPeer> {
|
||||
let node_record = match self.try_into_reachable(enr, socket) {
|
||||
Ok(enr_bc) => enr_bc,
|
||||
Err(err) => {
|
||||
trace!(target: "net::discovery::discv5",
|
||||
%err,
|
||||
?enr,
|
||||
"discovered peer is unreachable"
|
||||
);
|
||||
|
||||
self.metrics.discovered_peers.increment_established_sessions_unreachable_enr(1);
|
||||
|
||||
return None
|
||||
}
|
||||
};
|
||||
if let FilterOutcome::Ignore { reason } = self.filter_discovered_peer(enr) {
|
||||
trace!(target: "net::discovery::discv5",
|
||||
?enr,
|
||||
reason,
|
||||
"filtered out discovered peer"
|
||||
);
|
||||
|
||||
self.metrics.discovered_peers.increment_established_sessions_filtered(1);
|
||||
|
||||
return None
|
||||
}
|
||||
|
||||
// todo: extend for all network stacks in reth-network rlpx logic
|
||||
let fork_id = (self.fork_key == Some(NetworkStackId::ETH))
|
||||
.then(|| self.get_fork_id(enr).ok())
|
||||
.flatten();
|
||||
|
||||
trace!(target: "net::discovery::discv5",
|
||||
?fork_id,
|
||||
?enr,
|
||||
"discovered peer"
|
||||
);
|
||||
|
||||
Some(DiscoveredPeer { node_record, fork_id })
|
||||
}
|
||||
|
||||
/// Tries to convert an [`Enr`](discv5::Enr) into the backwards compatible type [`NodeRecord`],
|
||||
/// w.r.t. local [`IpMode`]. Tries the socket from which the ENR was sent, if socket is missing
|
||||
/// from ENR.
|
||||
///
|
||||
/// Note: [`discv5::Discv5`] won't initiate a session with any peer with a malformed node
|
||||
/// record, that advertises a reserved IP address on a WAN network.
|
||||
pub fn try_into_reachable(
|
||||
&self,
|
||||
enr: &discv5::Enr,
|
||||
socket: SocketAddr,
|
||||
) -> Result<NodeRecord, Error> {
|
||||
let id = enr_to_discv4_id(enr).ok_or(Error::IncompatibleKeyType)?;
|
||||
|
||||
let udp_socket = self.ip_mode().get_contactable_addr(enr).unwrap_or(socket);
|
||||
|
||||
// since we, on bootstrap, set tcp4 in local ENR for `IpMode::Dual`, we prefer tcp4 here
|
||||
// too
|
||||
let Some(tcp_port) = (match self.ip_mode() {
|
||||
IpMode::Ip4 | IpMode::DualStack => enr.tcp4(),
|
||||
IpMode::Ip6 => enr.tcp6(),
|
||||
}) else {
|
||||
return Err(Error::IpVersionMismatchRlpx(self.ip_mode()))
|
||||
};
|
||||
|
||||
Ok(NodeRecord { address: udp_socket.ip(), tcp_port, udp_port: udp_socket.port(), id })
|
||||
}
|
||||
|
||||
/// Applies filtering rules on an ENR. Returns [`Ok`](FilterOutcome::Ok) if peer should be
|
||||
/// passed up to app, and [`Ignore`](FilterOutcome::Ignore) if peer should instead be dropped.
|
||||
pub fn filter_discovered_peer(&self, enr: &discv5::Enr) -> FilterOutcome {
|
||||
self.discovered_peer_filter.filter(enr)
|
||||
}
|
||||
|
||||
/// Returns the [`ForkId`] of the given [`Enr`](discv5::Enr) w.r.t. the local node's network
|
||||
/// stack, if field is set.
|
||||
pub fn get_fork_id<K: discv5::enr::EnrKey>(
|
||||
&self,
|
||||
enr: &discv5::enr::Enr<K>,
|
||||
) -> Result<ForkId, Error> {
|
||||
let Some(key) = self.fork_key else { return Err(Error::NetworkStackIdNotConfigured) };
|
||||
let fork_id = enr
|
||||
.get_decodable::<EnrForkIdEntry>(key)
|
||||
.ok_or(Error::ForkMissing(key))?
|
||||
.map(Into::into)?;
|
||||
|
||||
Ok(fork_id)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface with sigp/discv5
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Exposes API of [`discv5::Discv5`].
|
||||
pub fn with_discv5<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&discv5::Discv5) -> R,
|
||||
{
|
||||
f(&self.discv5)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Complementary
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns the [`IpMode`] of the local node.
|
||||
pub fn ip_mode(&self) -> IpMode {
|
||||
self.ip_mode
|
||||
}
|
||||
|
||||
/// Returns the key to use to identify the [`ForkId`] kv-pair on the [`Enr`](discv5::Enr).
|
||||
pub fn fork_key(&self) -> Option<&[u8]> {
|
||||
self.fork_key
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Discv5 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"{ .. }".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of successfully processing a peer discovered by [`discv5::Discv5`].
|
||||
#[derive(Debug)]
|
||||
pub struct DiscoveredPeer {
|
||||
/// A discovery v4 backwards compatible ENR.
|
||||
pub node_record: NodeRecord,
|
||||
/// [`ForkId`] extracted from ENR w.r.t. configured
|
||||
pub fork_id: Option<ForkId>,
|
||||
}
|
||||
|
||||
/// Builds the local ENR with the supplied key.
|
||||
pub fn build_local_enr(
|
||||
sk: &SecretKey,
|
||||
config: &Config,
|
||||
) -> (Enr<SecretKey>, NodeRecord, Option<&'static [u8]>, IpMode) {
|
||||
@ -284,7 +454,7 @@ impl Discv5 {
|
||||
}
|
||||
|
||||
/// Bootstraps underlying [`discv5::Discv5`] node with configured peers.
|
||||
async fn bootstrap(
|
||||
pub async fn bootstrap(
|
||||
bootstrap_nodes: HashSet<BootNode>,
|
||||
discv5: &Arc<discv5::Discv5>,
|
||||
) -> Result<(), Error> {
|
||||
@ -321,7 +491,7 @@ impl Discv5 {
|
||||
}
|
||||
|
||||
/// Backgrounds regular look up queries, in order to keep kbuckets populated.
|
||||
fn spawn_populate_kbuckets_bg(
|
||||
pub fn spawn_populate_kbuckets_bg(
|
||||
lookup_interval: u64,
|
||||
bootstrap_lookup_interval: u64,
|
||||
bootstrap_lookup_countdown: u64,
|
||||
@ -382,175 +552,6 @@ impl Discv5 {
|
||||
});
|
||||
}
|
||||
|
||||
/// Process an event from the underlying [`discv5::Discv5`] node.
|
||||
pub fn on_discv5_update(&mut self, update: discv5::Event) -> Option<DiscoveredPeer> {
|
||||
match update {
|
||||
discv5::Event::SocketUpdated(_) | discv5::Event::TalkRequest(_) |
|
||||
// `Discovered` not unique discovered peers
|
||||
discv5::Event::Discovered(_) => None,
|
||||
discv5::Event::NodeInserted { replaced: _, .. } => {
|
||||
|
||||
// node has been inserted into kbuckets
|
||||
|
||||
// `replaced` partly covers `reth_discv4::DiscoveryUpdate::Removed(_)`
|
||||
|
||||
self.metrics.discovered_peers.increment_kbucket_insertions(1);
|
||||
|
||||
None
|
||||
}
|
||||
discv5::Event::SessionEstablished(enr, remote_socket) => {
|
||||
// covers `reth_discv4::DiscoveryUpdate` equivalents `DiscoveryUpdate::Added(_)`
|
||||
// and `DiscoveryUpdate::DiscoveredAtCapacity(_)
|
||||
|
||||
// peer has been discovered as part of query, or, by incoming session (peer has
|
||||
// discovered us)
|
||||
|
||||
self.metrics.discovered_peers_advertised_networks.increment_once_by_network_type(&enr);
|
||||
|
||||
self.metrics.discovered_peers.increment_established_sessions_raw(1);
|
||||
|
||||
self.on_discovered_peer(&enr, remote_socket)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes a discovered peer. Returns `true` if peer is added to
|
||||
fn on_discovered_peer(
|
||||
&mut self,
|
||||
enr: &discv5::Enr,
|
||||
socket: SocketAddr,
|
||||
) -> Option<DiscoveredPeer> {
|
||||
let node_record = match self.try_into_reachable(enr, socket) {
|
||||
Ok(enr_bc) => enr_bc,
|
||||
Err(err) => {
|
||||
trace!(target: "net::discovery::discv5",
|
||||
%err,
|
||||
?enr,
|
||||
"discovered peer is unreachable"
|
||||
);
|
||||
|
||||
self.metrics.discovered_peers.increment_established_sessions_unreachable_enr(1);
|
||||
|
||||
return None
|
||||
}
|
||||
};
|
||||
if let FilterOutcome::Ignore { reason } = self.filter_discovered_peer(enr) {
|
||||
trace!(target: "net::discovery::discv5",
|
||||
?enr,
|
||||
reason,
|
||||
"filtered out discovered peer"
|
||||
);
|
||||
|
||||
self.metrics.discovered_peers.increment_established_sessions_filtered(1);
|
||||
|
||||
return None
|
||||
}
|
||||
|
||||
// todo: extend for all network stacks in reth-network rlpx logic
|
||||
let fork_id = (self.fork_key == Some(NetworkStackId::ETH))
|
||||
.then(|| self.get_fork_id(enr).ok())
|
||||
.flatten();
|
||||
|
||||
trace!(target: "net::discovery::discv5",
|
||||
?fork_id,
|
||||
?enr,
|
||||
"discovered peer"
|
||||
);
|
||||
|
||||
Some(DiscoveredPeer { node_record, fork_id })
|
||||
}
|
||||
|
||||
/// Tries to convert an [`Enr`](discv5::Enr) into the backwards compatible type [`NodeRecord`],
|
||||
/// w.r.t. local [`IpMode`]. Tries the socket from which the ENR was sent, if socket is missing
|
||||
/// from ENR.
|
||||
///
|
||||
/// Note: [`discv5::Discv5`] won't initiate a session with any peer with a malformed node
|
||||
/// record, that advertises a reserved IP address on a WAN network.
|
||||
fn try_into_reachable(
|
||||
&self,
|
||||
enr: &discv5::Enr,
|
||||
socket: SocketAddr,
|
||||
) -> Result<NodeRecord, Error> {
|
||||
let id = enr_to_discv4_id(enr).ok_or(Error::IncompatibleKeyType)?;
|
||||
|
||||
let udp_socket = self.ip_mode().get_contactable_addr(enr).unwrap_or(socket);
|
||||
|
||||
// since we, on bootstrap, set tcp4 in local ENR for `IpMode::Dual`, we prefer tcp4 here
|
||||
// too
|
||||
let Some(tcp_port) = (match self.ip_mode() {
|
||||
IpMode::Ip4 | IpMode::DualStack => enr.tcp4(),
|
||||
IpMode::Ip6 => enr.tcp6(),
|
||||
}) else {
|
||||
return Err(Error::IpVersionMismatchRlpx(self.ip_mode()))
|
||||
};
|
||||
|
||||
Ok(NodeRecord { address: udp_socket.ip(), tcp_port, udp_port: udp_socket.port(), id })
|
||||
}
|
||||
|
||||
/// Applies filtering rules on an ENR. Returns [`Ok`](FilterOutcome::Ok) if peer should be
|
||||
/// passed up to app, and [`Ignore`](FilterOutcome::Ignore) if peer should instead be dropped.
|
||||
fn filter_discovered_peer(&self, enr: &discv5::Enr) -> FilterOutcome {
|
||||
self.discovered_peer_filter.filter(enr)
|
||||
}
|
||||
|
||||
/// Returns the [`ForkId`] of the given [`Enr`](discv5::Enr) w.r.t. the local node's network
|
||||
/// stack, if field is set.
|
||||
fn get_fork_id<K: discv5::enr::EnrKey>(
|
||||
&self,
|
||||
enr: &discv5::enr::Enr<K>,
|
||||
) -> Result<ForkId, Error> {
|
||||
let Some(key) = self.fork_key else { return Err(Error::NetworkStackIdNotConfigured) };
|
||||
let fork_id = enr
|
||||
.get_decodable::<EnrForkIdEntry>(key)
|
||||
.ok_or(Error::ForkMissing(key))?
|
||||
.map(Into::into)?;
|
||||
|
||||
Ok(fork_id)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface with sigp/discv5
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Exposes API of [`discv5::Discv5`].
|
||||
pub fn with_discv5<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&discv5::Discv5) -> R,
|
||||
{
|
||||
f(&self.discv5)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Complementary
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns the [`IpMode`] of the local node.
|
||||
pub fn ip_mode(&self) -> IpMode {
|
||||
self.ip_mode
|
||||
}
|
||||
|
||||
/// Returns the key to use to identify the [`ForkId`] kv-pair on the [`Enr`](discv5::Enr).
|
||||
pub fn fork_key(&self) -> Option<&[u8]> {
|
||||
self.fork_key
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Discv5 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"{ .. }".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of successfully processing a peer discovered by [`discv5::Discv5`].
|
||||
#[derive(Debug)]
|
||||
pub struct DiscoveredPeer {
|
||||
/// A discovery v4 backwards compatible ENR.
|
||||
pub node_record: NodeRecord,
|
||||
/// [`ForkId`] extracted from ENR w.r.t. configured
|
||||
pub fork_id: Option<ForkId>,
|
||||
}
|
||||
|
||||
/// Gets the next lookup target, based on which bucket is currently being targeted.
|
||||
pub fn get_lookup_target(
|
||||
kbucket_index: usize,
|
||||
@ -846,7 +847,7 @@ mod tests {
|
||||
let config = Config::builder(TCP_PORT).fork(NetworkStackId::ETH, fork_id).build();
|
||||
|
||||
let sk = SecretKey::new(&mut thread_rng());
|
||||
let (enr, _, _, _) = Discv5::build_local_enr(&sk, &config);
|
||||
let (enr, _, _, _) = build_local_enr(&sk, &config);
|
||||
|
||||
let decoded_fork_id = enr
|
||||
.get_decodable::<EnrForkIdEntry>(NetworkStackId::ETH)
|
||||
|
||||
Reference in New Issue
Block a user