From 539aee95e1109e9604d29d725ccb38f348fe0e77 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 18 Mar 2023 16:32:25 +0100 Subject: [PATCH 001/154] Plug-in hole punching --- Cargo.toml | 2 + src/handler/mod.rs | 373 +++++++++++++++++++++++++++++++++++++++------ src/packet/mod.rs | 2 +- 3 files changed, 332 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b7c880b98..4b8454731 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ categories = ["network-programming", "asynchronous"] exclude = [".gitignore", ".github/*"] [dependencies] +# nat_hole_punch = { package = "nat_hole_punch", git = "https://github.com/emhane/nat_hole_punch", branch = "nat-hole-punch" } enr = { version = "0.7.0", features = ["k256", "ed25519"] } tokio = { version = "1.15.0", features = ["net", "sync", "macros", "rt"] } tokio-stream = "0.1.8" @@ -39,6 +40,7 @@ lru = "0.7.1" hashlink = "0.7.0" delay_map = "0.3.0" more-asserts = "0.2.2" +async-trait = "0.1.67" [dev-dependencies] rand_07 = { package = "rand", version = "0.7" } diff --git a/src/handler/mod.rs b/src/handler/mod.rs index dacf78f8f..2f07964e8 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -36,14 +36,17 @@ use crate::{ socket::{FilterConfig, Socket}, Enr, }; +use async_trait::async_trait; use delay_map::HashMapDelay; use enr::{CombinedKey, NodeId}; use futures::prelude::*; +//use nat_hole_punch::*; use parking_lot::RwLock; use std::{ collections::HashMap, convert::TryFrom, default::Default, + marker::PhantomData, net::SocketAddr, pin::Pin, sync::{atomic::Ordering, Arc}, @@ -170,7 +173,7 @@ struct PendingRequest { } /// Process to handle handshakes and sessions established from raw RPC communications between nodes. -pub struct Handler { +pub struct Handler { /// Configuration for the discv5 service. request_retries: u8, /// The local node id to save unnecessary read locks on the ENR. The NodeID should not change @@ -200,6 +203,7 @@ pub struct Handler { socket: Socket, /// Exit channel to shutdown the handler. exit: oneshot::Receiver<()>, + _phantom: PhantomData

, } type HandlerReturn = ( @@ -208,9 +212,9 @@ type HandlerReturn = ( mpsc::Receiver, ); -impl Handler { +impl Handler

{ /// A new Session service which instantiates the UDP socket send/recv tasks. - pub async fn spawn( + pub async fn spawn( enr: Arc>, key: Arc>, listen_socket: SocketAddr, @@ -256,7 +260,7 @@ impl Handler { .clone() .expect("Executor must be present") .spawn(Box::pin(async move { - let mut handler = Handler { + let mut handler: Handler

= Handler { request_retries: config.request_retries, node_id, enr, @@ -274,16 +278,17 @@ impl Handler { listen_socket, socket, exit, + _phantom: PhantomData, }; debug!("Handler Starting"); - handler.start::

().await; + handler.start().await; })); Ok((exit_sender, handler_send, handler_recv)) } /// The main execution loop for the handler. - async fn start(&mut self) { + async fn start(&mut self) { let mut banned_nodes_check = tokio::time::interval(Duration::from_secs(BANNED_NODES_CHECK)); loop { @@ -292,19 +297,19 @@ impl Handler { match handler_request { HandlerIn::Request(contact, request) => { let Request { id, body: request } = *request; - if let Err(request_error) = self.send_request::

(contact, HandlerReqId::External(id.clone()), request).await { + if let Err(request_error) = self.send_request(contact, HandlerReqId::External(id.clone()), request).await { // If the sending failed report to the application if let Err(e) = self.service_send.send(HandlerOut::RequestFailed(id, request_error)).await { warn!("Failed to inform that request failed {}", e) } } } - HandlerIn::Response(dst, response) => self.send_response::

(dst, *response).await, - HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge::

(wru_ref, enr).await, + HandlerIn::Response(dst, response) => self.send_response(dst, *response).await, + HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge(wru_ref, enr).await, } } Some(inbound_packet) = self.socket.recv.recv() => { - self.process_inbound_packet::

(inbound_packet).await; + self.process_inbound_packet(inbound_packet).await; } Some(Ok((node_address, pending_request))) = self.active_requests.next() => { self.handle_request_timeout(node_address, pending_request).await; @@ -312,7 +317,7 @@ impl Handler { Some(Ok((node_address, _challenge))) = self.active_challenges.next() => { // A challenge has expired. There could be pending requests awaiting this // challenge. We process them here - self.send_next_request::

(node_address).await; + self.send_next_request(node_address).await; } _ = banned_nodes_check.tick() => self.unban_nodes_check(), // Unban nodes that are past the timeout _ = &mut self.exit => { @@ -323,17 +328,14 @@ impl Handler { } /// Processes an inbound decoded packet. - async fn process_inbound_packet( - &mut self, - inbound_packet: socket::InboundPacket, - ) { + async fn process_inbound_packet(&mut self, inbound_packet: socket::InboundPacket) { let message_nonce = inbound_packet.header.message_nonce; match inbound_packet.header.kind { PacketKind::WhoAreYou { enr_seq, .. } => { let challenge_data = ChallengeData::try_from(inbound_packet.authenticated_data.as_slice()) .expect("Must be correct size"); - self.handle_challenge::

( + self.handle_challenge( inbound_packet.src_address, message_nonce, enr_seq, @@ -351,7 +353,7 @@ impl Handler { socket_addr: inbound_packet.src_address, node_id: src_id, }; - self.handle_auth_message::

( + self.handle_auth_message( node_address, message_nonce, &id_nonce_sig, @@ -367,7 +369,7 @@ impl Handler { socket_addr: inbound_packet.src_address, node_id: src_id, }; - self.handle_message::

( + self.handle_message( node_address, message_nonce, &inbound_packet.message, @@ -375,6 +377,32 @@ impl Handler { ) .await } + PacketKind::Notification { src_id } => { + let node_address = NodeAddress { + socket_addr: inbound_packet.src_address, + node_id: src_id, + }; + match ::on_notification( + self, + node_address, + message_nonce, + &inbound_packet.message, + &inbound_packet.authenticated_data, + ) + .await + { + Ok(()) => { + trace!("Received notification from: {}", node_address); + } + Err(e) => { + warn!( + "Failed to process notification packet from: {}. Error: {:?}", + node_address, e + ); + return; + } + } + } } } @@ -426,7 +454,7 @@ impl Handler { } /// Sends a `Request` to a node. - async fn send_request( + async fn send_request( &mut self, contact: NodeContact, request_id: HandlerReqId, @@ -497,11 +525,7 @@ impl Handler { } /// Sends an RPC Response. - async fn send_response( - &mut self, - node_address: NodeAddress, - response: Response, - ) { + async fn send_response(&mut self, node_address: NodeAddress, response: Response) { // Check for an established session if let Some(session) = self.sessions.get_mut(&node_address) { // Encrypt the message and send @@ -525,11 +549,7 @@ impl Handler { /// This is called in response to a `HandlerOut::WhoAreYou` event. The applications finds the /// highest known ENR for a node then we respond to the node with a WHOAREYOU packet. - async fn send_challenge( - &mut self, - wru_ref: WhoAreYouRef, - remote_enr: Option, - ) { + async fn send_challenge(&mut self, wru_ref: WhoAreYouRef, remote_enr: Option) { let node_address = wru_ref.0; let message_nonce = wru_ref.1; @@ -569,7 +589,7 @@ impl Handler { /* Packet Handling */ /// Handles a WHOAREYOU packet that was received from the network. - async fn handle_challenge( + async fn handle_challenge( &mut self, src_address: SocketAddr, request_nonce: MessageNonce, @@ -713,7 +733,7 @@ impl Handler { let request = RequestBody::FindNode { distances: vec![0] }; session.awaiting_enr = Some(id.clone()); if let Err(e) = self - .send_request::

(contact, HandlerReqId::Internal(id), request) + .send_request(contact, HandlerReqId::Internal(id), request) .await { warn!("Failed to send Enr request {}", e) @@ -741,7 +761,7 @@ impl Handler { /// Handle a message that contains an authentication header. #[allow(clippy::too_many_arguments)] - async fn handle_auth_message( + async fn handle_auth_message( &mut self, node_address: NodeAddress, message_nonce: MessageNonce, @@ -789,7 +809,7 @@ impl Handler { warn!("Failed to inform of established session {}", e) } self.new_session(node_address.clone(), session); - self.handle_message::

( + self.handle_message( node_address.clone(), message_nonce, message, @@ -798,7 +818,7 @@ impl Handler { .await; // We could have pending messages that were awaiting this session to be // established. If so process them. - self.send_next_request::

(node_address).await; + self.send_next_request(node_address).await; } else { // IP's or NodeAddress don't match. Drop the session. warn!( @@ -836,7 +856,7 @@ impl Handler { } } - async fn send_next_request(&mut self, node_address: NodeAddress) { + async fn send_next_request(&mut self, node_address: NodeAddress) { // ensure we are not over writing any existing requests if self.active_requests.get(&node_address).is_none() { if let std::collections::hash_map::Entry::Occupied(mut entry) = @@ -853,7 +873,7 @@ impl Handler { } trace!("Sending next awaiting message. Node: {}", contact); if let Err(request_error) = self - .send_request::

(contact, request_id.clone(), request) + .send_request(contact, request_id.clone(), request) .await { warn!("Failed to send next awaiting request {}", request_error); @@ -880,7 +900,7 @@ impl Handler { /// Handle a standard message that does not contain an authentication header. #[allow(clippy::single_match)] - async fn handle_message( + async fn handle_message( &mut self, node_address: NodeAddress, message_nonce: MessageNonce, @@ -982,7 +1002,7 @@ impl Handler { } } // Handle standard responses - self.handle_response::

(node_address, response).await; + self.handle_response(node_address, response).await; } } } else { @@ -1006,11 +1026,7 @@ impl Handler { /// Handles a response to a request. Re-inserts the request call if the response is a multiple /// Nodes response. - async fn handle_response( - &mut self, - node_address: NodeAddress, - response: Response, - ) { + async fn handle_response(&mut self, node_address: NodeAddress, response: Response) { // Find a matching request, if any if let Some(mut request_call) = self.active_requests.remove(&node_address) { let id = match request_call.id() { @@ -1081,7 +1097,7 @@ impl Handler { { warn!("Failed to inform of response {}", e) } - self.send_next_request::

(node_address).await; + self.send_next_request(node_address).await; } else { // This is likely a late response and we have already failed the request. These get // dropped here. @@ -1193,3 +1209,272 @@ impl Handler { .retain(|_, time| time.is_none() || Some(Instant::now()) < *time); } } + +/// Discv5 message nonce length in bytes. +pub const MESSAGE_NONCE_LENGTH: usize = 12; +pub type NonceOfTimedOutMessage = MessageNonce; + +macro_rules! impl_from_variant_wrap { + ($from_type: ty, $to_type: ty, $variant: path) => { + impl From<$from_type> for $to_type { + fn from(e: $from_type) -> Self { + $variant(e) + } + } + }; +} + +#[derive(Debug)] +pub enum HolePunchError { + NotificationError(rlp::DecoderError), + Session(String), + RelayError(String), + TargetError(String), +} + +#[async_trait] +pub trait NatHolePunch { + /// A FINDNODE request, as part of a find node query, has timed out. Hole punching is + /// initiated. The node which passed the hole punch target peer in a NODES response to us is + /// used as relay. + async fn on_time_out( + &mut self, + relay: NodeAddress, + notif: RelayInit, + ) -> Result<(), HolePunchError>; + /// Handle a notification received over discv5 used for hole punching. + async fn on_notification_packet( + &mut self, + notif_sender: NodeAddress, + notif_nonce: MessageNonce, + notif: &[u8], + authenticated_data: &[u8], + ) -> Result<(), HolePunchError> { + let decrypted_notif = self + .decrypt_notification(notif_sender, notif_nonce, notif, authenticated_data) + .await?; + + match Notification::decode(decrypted_notif)? { + Notification::RelayInit(relay_init_notif) => self.on_relay_init(relay_init_notif).await, + Notification::RelayMsg(relay_msg_notif) => self.on_relay_msg(relay_msg_notif).await, + } + } + /// Decrypt a notification with the session keys held for the sender, just like for a discv5 + /// message. Notifications differentiate themsleves from discv5 messages (request or response) + /// in the way they handle a session, or rather the absence of a session. The duration of a + /// roundtrip in a hole punch relay circuit is bound by the duration of an average router's + /// time out for a udp entry for a given connection. Notifications that can't be decrypted + /// should be dropped to stay within bounds. + async fn decrypt_notification( + &mut self, + notif_sender: NodeAddress, + notif_nonce: MessageNonce, + notif: &[u8], + authenticated_data: &[u8], + ) -> Result, HolePunchError>; + /// This node receives a message to relay. + async fn on_relay_init(&mut self, notif: RelayInit) -> Result<(), HolePunchError>; + /// This node received a relayed message and should punch a hole in its NAT for the initiator. + async fn on_relay_msg(&mut self, notif: RelayMsg) -> Result<(), HolePunchError>; +} + +/// A unicast notification sent over discv5. +pub enum Notification { + /// Initialise a one-shot relay circuit. + RelayInit(RelayInit), + /// A relayed notification. + RelayMsg(RelayMsg), +} + +/// A hole punch notification sent to the relay. Contains the node address of the initiator of the +/// hole punch, the nonce of the request from the initiator to the target that triggered +/// `on_time_out` and the node address of the hole punch target peer. +pub struct RelayInit(NodeAddress, NonceOfTimedOutMessage, NodeAddress); + +impl RelayInit { + pub fn encode(self) -> Result, HolePunchError> { + todo!() + } + pub fn decode(bytes: Vec) -> Result { + todo!() + } +} + +/// A relayed hole punch notification sent to the target. Contains the node address of the +/// initiator of the hole punch and the nonce of the initiator's request that timed out, so the +/// hole punch target peer can respond with WHOAREYOU to the initiator. +pub struct RelayMsg(NodeAddress, NonceOfTimedOutMessage); + +impl RelayMsg { + pub fn encode(self) -> Result, HolePunchError> { + todo!() + } + pub fn decode(bytes: Vec) -> Result { + todo!() + } +} + +impl_from_variant_wrap!(RelayInit, Notification, Self::RelayInit); +impl_from_variant_wrap!(RelayMsg, Notification, Self::RelayMsg); + +impl Notification { + fn decode(message: Vec) -> Result { + // check flag todo(emhane) + todo!() + } + fn encode(self) -> Result, HolePunchError> { + /// Encodes a Message to RLP-encoded bytes. + let mut buf = Vec::with_capacity(10); + /*let msg_type = self.msg_type(); + buf.push(msg_type); + let id = &self.id; + match self.body { + RequestBody::Ping { enr_seq } => { + let mut s = RlpStream::new(); + s.begin_list(2); + s.append(&id.as_bytes()); + s.append(&enr_seq); + buf.extend_from_slice(&s.out()); + buf + } + RequestBody::FindNode { distances } => { + let mut s = RlpStream::new(); + s.begin_list(2); + s.append(&id.as_bytes()); + s.begin_list(distances.len()); + for distance in distances { + s.append(&distance); + } + buf.extend_from_slice(&s.out()); + buf + } + }*/ + Ok(buf) + } +} + +#[async_trait] +impl NatHolePunch for Handler

{ + async fn on_time_out( + &mut self, + relay: NodeAddress, + notif: RelayInit, + ) -> Result<(), HolePunchError> { + if let Some(session) = self.sessions.get_mut(&relay) { + // Encrypt the message and send + let packet = match session.encrypt_message(self.node_id, ¬if.encode()?) { + Ok(packet) => packet, + Err(e) => { + return Err(HolePunchError::RelayError(format!( + "Could not encrypt notification: {:?}", + e + ))); + } + }; + self.send(relay, packet).await; + } else { + // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays + // within the time out of the udp entrypoint for the target peer in the intiator's + // router, set by the original timed out FINDNODE request from the initiator, as the + // initiator may also be behind a NAT. + warn!( + "Session is not established. Dropping relay notification for relay: {}", + relay.node_id + ); + } + Ok(()) + } + + async fn decrypt_notification( + &mut self, + session_index: NodeAddress, + notif_nonce: MessageNonce, + notif: &[u8], + authenticated_data: &[u8], + ) -> Result, HolePunchError> { + // check if we have an available session + match self.sessions.get_mut(&session_index) { + Some(session) => { + // attempt to decrypt + match session.decrypt_message(notif_nonce, notif, authenticated_data) { + Err(e) => { + // We have a session, but the notification could not be decrypted. It is + // likely the node sending this message has dropped their session. Since + // this is a notification, we do not reply with a WHOAREYOU to this random + // packet. This means we drop the current session and the notification. + self.fail_session(&session_index, RequestError::InvalidRemotePacket, true) + .await; + return Err(HolePunchError::Session(format!( + "Decryption of notification from node: {} failed. Dropping notification. Error: {:?}", + session_index, e + ))); + } + Ok(bytes) => Ok(bytes), + } + } + None => Err(HolePunchError::Session(format!( + "Received a notification without a session. Dropping notification from {}", + session_index + ))), + } + } + + async fn on_relay_init(&mut self, relay_init_notif: RelayInit) -> Result<(), HolePunchError> { + let RelayInit(initiator, nonce, target) = relay_init_notif; + let notif = Notification::RelayMsg(RelayMsg(initiator, nonce)); + // Check for an established session. Only relay to peers we have sessions with, + // otherwise it is unlikely we have passed the target node to the initiator in a NODES + // response recently. + if let Some(session) = self.sessions.get_mut(&target) { + // Encrypt the message and send + let packet = match session.encrypt_message(self.node_id, ¬if.encode()?) { + Ok(packet) => packet, + Err(e) => { + return Err(HolePunchError::RelayError(format!( + "Could not encrypt response: {:?}", + e + ))); + } + }; + self.send(target, packet).await; + } else { + // Either the session is being established or has expired. We simply drop the + // notification in this case to ensure hole punch round-trip time stays within the + // time out of the udp entrypoint for the target peer in the intiator's router, set by + // the original timed out FINDNODE request from the initiator, as the initiator may + // also be behind a NAT. + warn!( + "Session is not established. Dropping relayed notification for node: {}", + target.node_id + ); + } + Ok(()) + } + + async fn on_relay_msg(&mut self, notif: RelayMsg) -> Result<(), HolePunchError> { + let RelayMsg(initiator, nonce) = notif; + // A session may already have been established. + if self.sessions.get(&initiator).is_some() { + trace!("Session already established with initiator: {}", initiator); + return Ok(()); + } + // If we haven't already sent a WhoAreYou, possibly punching the same hole using another + // relay, spawn a WHOAREYOU event to punch a hole in our NAT for initiator. + if self.active_challenges.get(&initiator).is_none() { + let whoareyou_ref = WhoAreYouRef(initiator, nonce); + if let Err(e) = self + .service_send + .send(HandlerOut::WhoAreYou(whoareyou_ref)) + .await + { + return Err(HolePunchError::TargetError(format!( + "Failed to send WhoAreYou to the service {}", + e + ))); + } + } else { + trace!("WHOAREYOU packet already sent to initiator: {}", initiator); + } + Ok(()) + } +} diff --git a/src/packet/mod.rs b/src/packet/mod.rs index f071263bd..d74a4e431 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -36,7 +36,7 @@ impl ProtocolIdentity for DefaultProtocolId { const PROTOCOL_VERSION_BYTES: [u8; 2] = 0x0001_u16.to_be_bytes(); } -pub trait ProtocolIdentity { +pub trait ProtocolIdentity: Sync + Send { const PROTOCOL_ID_BYTES: [u8; 6]; const PROTOCOL_VERSION_BYTES: [u8; 2]; } From db81228c85cf50cff6c57b1fb3b50bf93c0ea871 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 20 Mar 2023 13:01:13 +0100 Subject: [PATCH 002/154] Notification message container --- src/packet/mod.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/packet/mod.rs b/src/packet/mod.rs index d74a4e431..4299e36db 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -141,6 +141,12 @@ pub enum PacketKind { /// The ENR record of the node if the WHOAREYOU request is out-dated. enr_record: Option, }, + /// A notification. Differs from [`PacketKind::Message`] in the way it handles sessions. If a + /// session doesn't exist to decrypt or encrypt a notification, it is dropped. + Notification { + /// The sending NodeId. + src_id: NodeId, + }, } impl From<&PacketKind> for u8 { @@ -149,6 +155,7 @@ impl From<&PacketKind> for u8 { PacketKind::Message { .. } => 0, PacketKind::WhoAreYou { .. } => 1, PacketKind::Handshake { .. } => 2, + PacketKind::Notification { .. } => 3, } } } @@ -157,7 +164,9 @@ impl PacketKind { /// Encodes the packet type into its corresponding auth_data. pub fn encode(&self) -> Vec { match self { - PacketKind::Message { src_id } => src_id.raw().to_vec(), + PacketKind::Message { src_id } | PacketKind::Notification { src_id } => { + src_id.raw().to_vec() + } PacketKind::WhoAreYou { id_nonce, enr_seq } => { let mut auth_data = Vec::with_capacity(24); auth_data.extend_from_slice(id_nonce); @@ -273,6 +282,16 @@ impl PacketKind { enr_record, }) } + 3 => { + // Decoding a notification packet + // This should only contain a 32 byte NodeId. + if auth_data.len() != 32 { + return Err(PacketError::InvalidAuthDataSize); + } + + let src_id = NodeId::parse(auth_data).map_err(|_| PacketError::InvalidNodeId)?; + Ok(PacketKind::Notification { src_id }) + } _ => Err(PacketError::UnknownPacket), } } @@ -362,7 +381,9 @@ impl Packet { pub fn is_whoareyou(&self) -> bool { match &self.header.kind { PacketKind::WhoAreYou { .. } => true, - PacketKind::Message { .. } | PacketKind::Handshake { .. } => false, + PacketKind::Message { .. } + | PacketKind::Handshake { .. } + | PacketKind::Notification { .. } => false, } } @@ -370,7 +391,7 @@ impl Packet { /// src_id in this case. pub fn src_id(&self) -> Option { match self.header.kind { - PacketKind::Message { src_id } => Some(src_id), + PacketKind::Message { src_id } | PacketKind::Notification { src_id } => Some(src_id), PacketKind::WhoAreYou { .. } => None, PacketKind::Handshake { src_id, .. } => Some(src_id), } @@ -565,6 +586,7 @@ impl std::fmt::Display for PacketKind { hex::encode(ephem_pubkey), enr_record ), + PacketKind::Notification { src_id } => write!(f, "Notification {{ src_id: {src_id} }}"), } } } From fdc792c14f167cc88c729dcb5e3ee3ee1fc6fd26 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 20 Mar 2023 14:37:14 +0100 Subject: [PATCH 003/154] Plug relaying into service --- src/handler/mod.rs | 126 +++++++++++++++++++++++++++------------------ src/service.rs | 90 ++++++++++++++++++++++++++------ 2 files changed, 152 insertions(+), 64 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 2f07964e8..33be3cbc6 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -105,6 +105,13 @@ pub enum HandlerIn { /// The `WhoAreYouRef` is sent out in the `HandlerOut::WhoAreYou` event and should /// be returned here to submit the application's response. WhoAreYou(WhoAreYouRef, Option), + + /// The node address of the relay, the local node address, the message nonce of the timed out + /// request and the node address of the target peer to hole punch and a rleay init + /// notification. A FINDNODE request to a peer discovered in a find node query has timed out. + /// The target of that request may be behind a NAT. The peer which passed us the target in a + /// NODES response, is used as relay to attmept punching a hole in the targets NAT. + HolePunch(NodeAddress, NodeAddress, MessageNonce, NodeAddress), } /// Messages sent between a node on the network and `Handler`. @@ -129,8 +136,10 @@ pub enum HandlerOut { /// An RPC request failed. /// - /// This returns the request ID and an error indicating why the request failed. - RequestFailed(RequestId, RequestError), + /// This returns the request ID, an error indicating why the request failed and possibly also + /// the message nonce to allow for attempting NAT hole punching (attempted upon request time + /// out). + RequestFailed(RequestId, RequestError, Option), } /// How we connected to the node. @@ -299,13 +308,22 @@ impl Handler

{ let Request { id, body: request } = *request; if let Err(request_error) = self.send_request(contact, HandlerReqId::External(id.clone()), request).await { // If the sending failed report to the application - if let Err(e) = self.service_send.send(HandlerOut::RequestFailed(id, request_error)).await { + if let Err(e) = self.service_send.send(HandlerOut::RequestFailed(id, request_error, None)).await { warn!("Failed to inform that request failed {}", e) } } } HandlerIn::Response(dst, response) => self.send_response(dst, *response).await, HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge(wru_ref, enr).await, + HandlerIn::HolePunch(relay, local_node_address, + message_nonce, + target_node_address) => { + if let Err(e) = self.on_time_out(relay, local_node_address, + message_nonce, + target_node_address).await { + warn!("Failed to start hole punching. Error: {:?}", e); + } + } } } Some(inbound_packet) = self.socket.recv.recv() => { @@ -382,25 +400,17 @@ impl Handler

{ socket_addr: inbound_packet.src_address, node_id: src_id, }; - match ::on_notification( - self, - node_address, - message_nonce, - &inbound_packet.message, - &inbound_packet.authenticated_data, - ) - .await + trace!("Received notification from: {}", node_address); + if let Err(e) = self + .on_notification( + node_address, + message_nonce, + &inbound_packet.message, + &inbound_packet.authenticated_data, + ) + .await { - Ok(()) => { - trace!("Received notification from: {}", node_address); - } - Err(e) => { - warn!( - "Failed to process notification packet from: {}. Error: {:?}", - node_address, e - ); - return; - } + warn!("Failed to process notification packet. Error: {:?}", e); } } } @@ -886,7 +896,7 @@ impl Handler

{ HandlerReqId::External(id) => { if let Err(e) = self .service_send - .send(HandlerOut::RequestFailed(id, request_error)) + .send(HandlerOut::RequestFailed(id, request_error, None)) .await { warn!("Failed to inform that request failed {}", e); @@ -1140,7 +1150,11 @@ impl Handler

{ HandlerReqId::External(id) => { if let Err(e) = self .service_send - .send(HandlerOut::RequestFailed(id.clone(), error.clone())) + .send(HandlerOut::RequestFailed( + id.clone(), + error.clone(), + Some(request_call.packet().header.message_nonce), + )) .await { warn!("Failed to inform request failure {}", e) @@ -1175,7 +1189,7 @@ impl Handler

{ HandlerReqId::External(id) => { if let Err(e) = self .service_send - .send(HandlerOut::RequestFailed(id, error.clone())) + .send(HandlerOut::RequestFailed(id, error.clone(), None)) .await { warn!("Failed to inform request failure {}", e) @@ -1240,10 +1254,12 @@ pub trait NatHolePunch { async fn on_time_out( &mut self, relay: NodeAddress, - notif: RelayInit, + local_node_address: NodeAddress, + message_nonce: MessageNonce, + target_node_address: NodeAddress, ) -> Result<(), HolePunchError>; - /// Handle a notification received over discv5 used for hole punching. - async fn on_notification_packet( + /// Handle a notification packet received over discv5 used for hole punching. + async fn on_notification( &mut self, notif_sender: NodeAddress, notif_nonce: MessageNonce, @@ -1251,7 +1267,7 @@ pub trait NatHolePunch { authenticated_data: &[u8], ) -> Result<(), HolePunchError> { let decrypted_notif = self - .decrypt_notification(notif_sender, notif_nonce, notif, authenticated_data) + .handle_decryption_with_session(notif_sender, notif_nonce, notif, authenticated_data) .await?; match Notification::decode(decrypted_notif)? { @@ -1259,15 +1275,14 @@ pub trait NatHolePunch { Notification::RelayMsg(relay_msg_notif) => self.on_relay_msg(relay_msg_notif).await, } } - /// Decrypt a notification with the session keys held for the sender, just like for a discv5 - /// message. Notifications differentiate themsleves from discv5 messages (request or response) - /// in the way they handle a session, or rather the absence of a session. The duration of a - /// roundtrip in a hole punch relay circuit is bound by the duration of an average router's - /// time out for a udp entry for a given connection. Notifications that can't be decrypted - /// should be dropped to stay within bounds. - async fn decrypt_notification( + /// Decrypt a notification with session keys held for the notification sender, just like for a + /// discv5 message. Notifications should differentiate themsleves from discv5 messages + /// (request or response) in the way they handle a session, or rather the absence of a + /// session. Notifications that can't be decrypted with existing session keys should be + /// dropped. + async fn handle_decryption_with_session( &mut self, - notif_sender: NodeAddress, + session_index: NodeAddress, // notif sender notif_nonce: MessageNonce, notif: &[u8], authenticated_data: &[u8], @@ -1289,7 +1304,8 @@ pub enum Notification { /// A hole punch notification sent to the relay. Contains the node address of the initiator of the /// hole punch, the nonce of the request from the initiator to the target that triggered /// `on_time_out` and the node address of the hole punch target peer. -pub struct RelayInit(NodeAddress, NonceOfTimedOutMessage, NodeAddress); +#[derive(Debug, Clone, PartialEq)] +pub struct RelayInit(pub NodeAddress, pub NonceOfTimedOutMessage, pub NodeAddress); impl RelayInit { pub fn encode(self) -> Result, HolePunchError> { @@ -1358,19 +1374,31 @@ impl NatHolePunch for Handler

{ async fn on_time_out( &mut self, relay: NodeAddress, - notif: RelayInit, + local_node_address: NodeAddress, + timed_out_message_nonce: MessageNonce, + target_node_address: NodeAddress, ) -> Result<(), HolePunchError> { + // Another hole punch process may have just completed. + if self.sessions.get(&target_node_address).is_some() { + return Ok(()); + } if let Some(session) = self.sessions.get_mut(&relay) { + let relay_init_notif = RelayInit( + local_node_address, + timed_out_message_nonce, + target_node_address, + ); // Encrypt the message and send - let packet = match session.encrypt_message(self.node_id, ¬if.encode()?) { - Ok(packet) => packet, - Err(e) => { - return Err(HolePunchError::RelayError(format!( - "Could not encrypt notification: {:?}", - e - ))); - } - }; + let packet = + match session.encrypt_message::

(self.node_id, &relay_init_notif.encode()?) { + Ok(packet) => packet, + Err(e) => { + return Err(HolePunchError::RelayError(format!( + "Could not encrypt notification: {:?}", + e + ))); + } + }; self.send(relay, packet).await; } else { // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays @@ -1385,7 +1413,7 @@ impl NatHolePunch for Handler

{ Ok(()) } - async fn decrypt_notification( + async fn handle_decryption_with_session( &mut self, session_index: NodeAddress, notif_nonce: MessageNonce, @@ -1427,7 +1455,7 @@ impl NatHolePunch for Handler

{ // response recently. if let Some(session) = self.sessions.get_mut(&target) { // Encrypt the message and send - let packet = match session.encrypt_message(self.node_id, ¬if.encode()?) { + let packet = match session.encrypt_message::

(self.node_id, ¬if.encode()?) { Ok(packet) => packet, Err(e) => { return Err(HolePunchError::RelayError(format!( diff --git a/src/service.rs b/src/service.rs index 342a186f5..1d2243ad0 100644 --- a/src/service.rs +++ b/src/service.rs @@ -25,7 +25,7 @@ use crate::{ NodeStatus, UpdateResult, MAX_NODES_PER_BUCKET, }, node_info::{NodeAddress, NodeContact, NonContactable}, - packet::{ProtocolIdentity, MAX_PACKET_SIZE}, + packet::{MessageNonce, ProtocolIdentity, MAX_PACKET_SIZE}, query_pool::{ FindNodeQueryConfig, PredicateQueryConfig, QueryId, QueryPool, QueryPoolState, TargetKey, }, @@ -199,6 +199,11 @@ pub struct Service { /// A channel that the service emits events on. event_stream: Option>, + + /// The last peer to send us a new peer in a NODES response is stored as the new peer's + /// potential relay until the first request to the new peer after its discovery is either + /// responded or failed. + new_peer_latest_relay: HashMap, } /// Active RPC request awaiting a response from the handler. @@ -263,7 +268,7 @@ impl Service { }; // build the session service - let (handler_exit, handler_send, handler_recv) = Handler::spawn::

( + let (handler_exit, handler_send, handler_recv) = Handler::

::spawn( local_enr.clone(), enr_key.clone(), listen_socket, @@ -296,6 +301,7 @@ impl Service { event_stream: None, exit, config: config.clone(), + new_peer_latest_relay: HashMap::default(), }; info!("Discv5 Service started"); @@ -373,13 +379,13 @@ impl Service { } } } - HandlerOut::RequestFailed(request_id, error) => { + HandlerOut::RequestFailed(request_id, error, message_nonce) => { if let RequestError::Timeout = error { debug!("RPC Request timed out. id: {}", request_id); } else { warn!("RPC Request failed: id: {}, error {:?}", request_id, error); } - self.rpc_failure(request_id, error); + self.rpc_failure(request_id, error, message_nonce); } } } @@ -643,6 +649,9 @@ impl Service { let node_id = node_address.node_id; + // Peer is not behind a NAT. Remove potential relay that may be stored for peer. + self.new_peer_latest_relay.remove(&node_id); + match response.body { ResponseBody::Nodes { total, mut nodes } => { if total > MAX_NODES_RESPONSES as u64 { @@ -695,7 +704,9 @@ impl Service { node_address ); let ban_timeout = self.config.ban_duration.map(|v| Instant::now() + v); - PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); + PERMIT_BAN_LIST + .write() + .ban(node_address.clone(), ban_timeout); nodes.retain(|enr| { peer_key.log2_distance(&enr.node_id().into()).is_none() }); @@ -716,7 +727,9 @@ impl Service { active_request.contact ); let ban_timeout = self.config.ban_duration.map(|v| Instant::now() + v); - PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); + PERMIT_BAN_LIST + .write() + .ban(node_address.clone(), ban_timeout); } } @@ -767,7 +780,7 @@ impl Service { // ensure any mapping is removed in this rare case self.active_nodes_responses.remove(&node_id); - self.discovered(&node_id, nodes, active_request.query_id); + self.discovered(&node_address, nodes, active_request.query_id); } ResponseBody::Pong { enr_seq, ip, port } => { let socket = SocketAddr::new(ip, port); @@ -1159,9 +1172,9 @@ impl Service { } /// Processes discovered peers from a query. - fn discovered(&mut self, source: &NodeId, mut enrs: Vec, query_id: Option) { + fn discovered(&mut self, source: &NodeAddress, mut enrs: Vec, query_id: Option) { let local_id = self.local_enr.read().node_id(); - enrs.retain(|enr| { + let discovered = enrs.retain(|enr| { if enr.node_id() == local_id { return false; } @@ -1174,7 +1187,8 @@ impl Service { // ignore peers that don't pass the table filter if (self.config.table_filter)(enr) { - let key = kbucket::Key::from(enr.node_id()); + let node_id = enr.node_id(); + let key = kbucket::Key::from(node_id); // If the ENR exists in the routing table and the discovered ENR has a greater // sequence number, perform some filter checks before updating the enr. @@ -1182,7 +1196,10 @@ impl Service { let must_update_enr = match self.kbuckets.write().entry(&key) { kbucket::Entry::Present(entry, _) => entry.value().seq() < enr.seq(), kbucket::Entry::Pending(mut entry, _) => entry.value().seq() < enr.seq(), - _ => false, + _ => { + self.new_peer_latest_relay.insert(node_id, source.clone()); + false + } }; if must_update_enr { @@ -1206,7 +1223,7 @@ impl Service { // requesting the target of the query, this ENR could be the result of requesting the // target-nodes own id. We don't want to add this as a "new" discovered peer in the // query, so we remove it from the discovered list here. - source != &enr.node_id() + source.node_id != enr.node_id() }); // if this is part of a query, update the query @@ -1225,7 +1242,7 @@ impl Service { peer_count += 1; } debug!("{} peers found for query id {:?}", peer_count, query_id); - query.on_success(source, &enrs) + query.on_success(&source.node_id, &enrs) } else { debug!("Response returned for ended query {:?}", query_id) } @@ -1364,7 +1381,12 @@ impl Service { /// A session could not be established or an RPC request timed-out (after a few retries, if /// specified). - fn rpc_failure(&mut self, id: RequestId, error: RequestError) { + fn rpc_failure( + &mut self, + id: RequestId, + error: RequestError, + message_nonce: Option, + ) { trace!("RPC Error removing request. Reason: {:?}, id {}", error, id); if let Some(active_request) = self.active_requests.remove(&id) { // If this is initiated by the user, return an error on the callback. All callbacks @@ -1393,6 +1415,44 @@ impl Service { // if a failed FindNodes request, ensure we haven't partially received packets. If // so, process the partially found nodes RequestBody::FindNode { .. } => { + let relay = self.new_peer_latest_relay.remove(&node_id); + if let RequestError::Timeout = error { + // The request might be timing out because the peer is behind a NAT. If we + // have a relay to the peer, attempt hole punching. + if let (Some(relay), Some(message_nonce)) = (relay, message_nonce) { + let target_node_address = active_request.contact.node_address(); + let local_enr = self.local_enr.read(); + let socket = match target_node_address.socket_addr { + SocketAddr::V4(_) => match local_enr.udp4_socket() { + Some(socket) => Some(SocketAddr::V4(socket)), + None => local_enr.udp6_socket().map(SocketAddr::V6), + }, + SocketAddr::V6(_) => match local_enr.udp6_socket() { + Some(socket) => Some(SocketAddr::V6(socket)), + None => local_enr.udp4_socket().map(SocketAddr::V4), + }, + }; + if let Some(socket_addr) = socket { + let local_node_address = NodeAddress { + socket_addr, + node_id: local_enr.node_id(), + }; + match self.handler_send.send(HandlerIn::HolePunch( + relay, + local_node_address, + message_nonce, + target_node_address, + )) { + Err(e) => { + warn!("Failed to start hole punch {}", e); + } + Ok(()) => { + return; + } + } + } + } + } if let Some(nodes_response) = self.active_nodes_responses.remove(&node_id) { if !nodes_response.received_nodes.is_empty() { warn!( @@ -1402,7 +1462,7 @@ impl Service { // if it's a query mark it as success, to process the partial // collection of peers self.discovered( - &node_id, + &active_request.contact.node_address(), nodes_response.received_nodes, active_request.query_id, ); From 9d223e5cb11044d9e2a0c8a5d2d7791f09e1992d Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 21 Mar 2023 19:20:05 +0100 Subject: [PATCH 004/154] Move hole punch trait to extern crate --- Cargo.toml | 2 +- src/handler/mod.rs | 151 +-------------------------------------------- src/node_info.rs | 3 +- 3 files changed, 6 insertions(+), 150 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4b8454731..90d50ed74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ categories = ["network-programming", "asynchronous"] exclude = [".gitignore", ".github/*"] [dependencies] -# nat_hole_punch = { package = "nat_hole_punch", git = "https://github.com/emhane/nat_hole_punch", branch = "nat-hole-punch" } +nat_hole_punch = { package = "nat_hole_punch", git = "https://github.com/emhane/nat_hole_punch", branch = "nat-hole-punch" } enr = { version = "0.7.0", features = ["k256", "ed25519"] } tokio = { version = "1.15.0", features = ["net", "sync", "macros", "rt"] } tokio-stream = "0.1.8" diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 33be3cbc6..0b15bfadc 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -40,7 +40,7 @@ use async_trait::async_trait; use delay_map::HashMapDelay; use enr::{CombinedKey, NodeId}; use futures::prelude::*; -//use nat_hole_punch::*; +use nat_hole_punch::*; use parking_lot::RwLock; use std::{ collections::HashMap, @@ -1224,151 +1224,6 @@ impl Handler

{ } } -/// Discv5 message nonce length in bytes. -pub const MESSAGE_NONCE_LENGTH: usize = 12; -pub type NonceOfTimedOutMessage = MessageNonce; - -macro_rules! impl_from_variant_wrap { - ($from_type: ty, $to_type: ty, $variant: path) => { - impl From<$from_type> for $to_type { - fn from(e: $from_type) -> Self { - $variant(e) - } - } - }; -} - -#[derive(Debug)] -pub enum HolePunchError { - NotificationError(rlp::DecoderError), - Session(String), - RelayError(String), - TargetError(String), -} - -#[async_trait] -pub trait NatHolePunch { - /// A FINDNODE request, as part of a find node query, has timed out. Hole punching is - /// initiated. The node which passed the hole punch target peer in a NODES response to us is - /// used as relay. - async fn on_time_out( - &mut self, - relay: NodeAddress, - local_node_address: NodeAddress, - message_nonce: MessageNonce, - target_node_address: NodeAddress, - ) -> Result<(), HolePunchError>; - /// Handle a notification packet received over discv5 used for hole punching. - async fn on_notification( - &mut self, - notif_sender: NodeAddress, - notif_nonce: MessageNonce, - notif: &[u8], - authenticated_data: &[u8], - ) -> Result<(), HolePunchError> { - let decrypted_notif = self - .handle_decryption_with_session(notif_sender, notif_nonce, notif, authenticated_data) - .await?; - - match Notification::decode(decrypted_notif)? { - Notification::RelayInit(relay_init_notif) => self.on_relay_init(relay_init_notif).await, - Notification::RelayMsg(relay_msg_notif) => self.on_relay_msg(relay_msg_notif).await, - } - } - /// Decrypt a notification with session keys held for the notification sender, just like for a - /// discv5 message. Notifications should differentiate themsleves from discv5 messages - /// (request or response) in the way they handle a session, or rather the absence of a - /// session. Notifications that can't be decrypted with existing session keys should be - /// dropped. - async fn handle_decryption_with_session( - &mut self, - session_index: NodeAddress, // notif sender - notif_nonce: MessageNonce, - notif: &[u8], - authenticated_data: &[u8], - ) -> Result, HolePunchError>; - /// This node receives a message to relay. - async fn on_relay_init(&mut self, notif: RelayInit) -> Result<(), HolePunchError>; - /// This node received a relayed message and should punch a hole in its NAT for the initiator. - async fn on_relay_msg(&mut self, notif: RelayMsg) -> Result<(), HolePunchError>; -} - -/// A unicast notification sent over discv5. -pub enum Notification { - /// Initialise a one-shot relay circuit. - RelayInit(RelayInit), - /// A relayed notification. - RelayMsg(RelayMsg), -} - -/// A hole punch notification sent to the relay. Contains the node address of the initiator of the -/// hole punch, the nonce of the request from the initiator to the target that triggered -/// `on_time_out` and the node address of the hole punch target peer. -#[derive(Debug, Clone, PartialEq)] -pub struct RelayInit(pub NodeAddress, pub NonceOfTimedOutMessage, pub NodeAddress); - -impl RelayInit { - pub fn encode(self) -> Result, HolePunchError> { - todo!() - } - pub fn decode(bytes: Vec) -> Result { - todo!() - } -} - -/// A relayed hole punch notification sent to the target. Contains the node address of the -/// initiator of the hole punch and the nonce of the initiator's request that timed out, so the -/// hole punch target peer can respond with WHOAREYOU to the initiator. -pub struct RelayMsg(NodeAddress, NonceOfTimedOutMessage); - -impl RelayMsg { - pub fn encode(self) -> Result, HolePunchError> { - todo!() - } - pub fn decode(bytes: Vec) -> Result { - todo!() - } -} - -impl_from_variant_wrap!(RelayInit, Notification, Self::RelayInit); -impl_from_variant_wrap!(RelayMsg, Notification, Self::RelayMsg); - -impl Notification { - fn decode(message: Vec) -> Result { - // check flag todo(emhane) - todo!() - } - fn encode(self) -> Result, HolePunchError> { - /// Encodes a Message to RLP-encoded bytes. - let mut buf = Vec::with_capacity(10); - /*let msg_type = self.msg_type(); - buf.push(msg_type); - let id = &self.id; - match self.body { - RequestBody::Ping { enr_seq } => { - let mut s = RlpStream::new(); - s.begin_list(2); - s.append(&id.as_bytes()); - s.append(&enr_seq); - buf.extend_from_slice(&s.out()); - buf - } - RequestBody::FindNode { distances } => { - let mut s = RlpStream::new(); - s.begin_list(2); - s.append(&id.as_bytes()); - s.begin_list(distances.len()); - for distance in distances { - s.append(&distance); - } - buf.extend_from_slice(&s.out()); - buf - } - }*/ - Ok(buf) - } -} - #[async_trait] impl NatHolePunch for Handler

{ async fn on_time_out( @@ -1423,7 +1278,7 @@ impl NatHolePunch for Handler

{ // check if we have an available session match self.sessions.get_mut(&session_index) { Some(session) => { - // attempt to decrypt + // attempt to decrypt notification (same decryption as for a message) match session.decrypt_message(notif_nonce, notif, authenticated_data) { Err(e) => { // We have a session, but the notification could not be decrypted. It is @@ -1454,7 +1309,7 @@ impl NatHolePunch for Handler

{ // otherwise it is unlikely we have passed the target node to the initiator in a NODES // response recently. if let Some(session) = self.sessions.get_mut(&target) { - // Encrypt the message and send + // Encrypt the notification and send (encrypted same way as a message) let packet = match session.encrypt_message::

(self.node_id, ¬if.encode()?) { Ok(packet) => packet, Err(e) => { diff --git a/src/node_info.rs b/src/node_info.rs index 1b299deb2..01a64879e 100644 --- a/src/node_info.rs +++ b/src/node_info.rs @@ -1,7 +1,8 @@ use super::*; use crate::Enr; use enr::{CombinedPublicKey, NodeId}; -use std::net::SocketAddr; +use rlp::{DecoderError, Rlp, RlpStream}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; #[cfg(feature = "libp2p")] use libp2p_core::{identity::PublicKey, multiaddr::Protocol, multihash, Multiaddr}; From 25a5cc5f0c2fcc5018c801546304434f1d4512f6 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 23 Mar 2023 11:20:07 +0100 Subject: [PATCH 005/154] Adjust impl of hole punch trait --- src/error.rs | 12 +++++ src/handler/mod.rs | 106 ++++++++++++++++++++++----------------------- src/node_info.rs | 29 ++++++++++++- 3 files changed, 92 insertions(+), 55 deletions(-) diff --git a/src/error.rs b/src/error.rs index d35ba90d4..788aa579a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -47,6 +47,18 @@ impl From for Discv5Error { } } +macro_rules! impl_from_to_variant { + ($(<$($generic: ident$(: $trait: path)*,)+>)*, $from_type: ty, $to_type: ty, $variant: path) => { + impl$(<$($generic$(: $trait)*,)+>)* From<$from_type> for $to_type { + fn from(_e: $from_type) -> Self { + $variant + } + } + }; +} + +impl_from_to_variant!(, tokio::sync::mpsc::error::SendError, Discv5Error, Self::ServiceChannelClosed); + #[derive(Debug, Clone, PartialEq, Eq)] /// Types of packet errors. pub enum PacketError { diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 0b15bfadc..b1d7f6045 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1226,32 +1226,31 @@ impl Handler

{ #[async_trait] impl NatHolePunch for Handler

{ + type TNodeAddress = NodeAddress; + type TDiscv5Error = Discv5Error; async fn on_time_out( &mut self, - relay: NodeAddress, - local_node_address: NodeAddress, + relay: Self::TNodeAddress, + local_node_address: Self::TNodeAddress, timed_out_message_nonce: MessageNonce, - target_node_address: NodeAddress, - ) -> Result<(), HolePunchError> { + target_node_address: Self::TNodeAddress, + ) -> Result<(), HolePunchError> { // Another hole punch process may have just completed. if self.sessions.get(&target_node_address).is_some() { return Ok(()); } if let Some(session) = self.sessions.get_mut(&relay) { let relay_init_notif = RelayInit( - local_node_address, + local_node_address.into(), timed_out_message_nonce, - target_node_address, + target_node_address.into(), ); // Encrypt the message and send let packet = - match session.encrypt_message::

(self.node_id, &relay_init_notif.encode()?) { + match session.encrypt_message::

(self.node_id, &relay_init_notif.rlp_encode()) { Ok(packet) => packet, Err(e) => { - return Err(HolePunchError::RelayError(format!( - "Could not encrypt notification: {:?}", - e - ))); + return Err(HolePunchError::RelayError(e)); } }; self.send(relay, packet).await; @@ -1270,11 +1269,11 @@ impl NatHolePunch for Handler

{ async fn handle_decryption_with_session( &mut self, - session_index: NodeAddress, + session_index: Self::TNodeAddress, notif_nonce: MessageNonce, notif: &[u8], authenticated_data: &[u8], - ) -> Result, HolePunchError> { + ) -> Result, HolePunchError> { // check if we have an available session match self.sessions.get_mut(&session_index) { Some(session) => { @@ -1287,76 +1286,77 @@ impl NatHolePunch for Handler

{ // packet. This means we drop the current session and the notification. self.fail_session(&session_index, RequestError::InvalidRemotePacket, true) .await; - return Err(HolePunchError::Session(format!( - "Decryption of notification from node: {} failed. Dropping notification. Error: {:?}", - session_index, e - ))); + return Err(HolePunchError::SessionError(e)); } Ok(bytes) => Ok(bytes), } } - None => Err(HolePunchError::Session(format!( - "Received a notification without a session. Dropping notification from {}", - session_index - ))), + None => Err(HolePunchError::SessionError( + Discv5Error::SessionNotEstablished, + )), } } - async fn on_relay_init(&mut self, relay_init_notif: RelayInit) -> Result<(), HolePunchError> { + async fn on_relay_init( + &mut self, + relay_init_notif: RelayInit, + ) -> Result<(), HolePunchError> { let RelayInit(initiator, nonce, target) = relay_init_notif; - let notif = Notification::RelayMsg(RelayMsg(initiator, nonce)); + let notif_for_target = RelayMsg(initiator, nonce); + // Overshadow with local version of same type + let target: NodeAddress = target.into(); // Check for an established session. Only relay to peers we have sessions with, // otherwise it is unlikely we have passed the target node to the initiator in a NODES // response recently. if let Some(session) = self.sessions.get_mut(&target) { // Encrypt the notification and send (encrypted same way as a message) - let packet = match session.encrypt_message::

(self.node_id, ¬if.encode()?) { - Ok(packet) => packet, - Err(e) => { - return Err(HolePunchError::RelayError(format!( - "Could not encrypt response: {:?}", - e - ))); - } - }; + let packet = + match session.encrypt_message::

(self.node_id, ¬if_for_target.rlp_encode()) { + Ok(packet) => packet, + Err(e) => { + return Err(HolePunchError::RelayError(e)); + } + }; self.send(target, packet).await; + Ok(()) } else { // Either the session is being established or has expired. We simply drop the // notification in this case to ensure hole punch round-trip time stays within the // time out of the udp entrypoint for the target peer in the intiator's router, set by // the original timed out FINDNODE request from the initiator, as the initiator may // also be behind a NAT. - warn!( - "Session is not established. Dropping relayed notification for node: {}", - target.node_id - ); + Err(HolePunchError::RelayError( + Discv5Error::SessionNotEstablished, + )) } - Ok(()) } - async fn on_relay_msg(&mut self, notif: RelayMsg) -> Result<(), HolePunchError> { + async fn on_relay_msg( + &mut self, + notif: RelayMsg, + ) -> Result<(), HolePunchError> { let RelayMsg(initiator, nonce) = notif; + // Overshadow with local version of same type + let initiator: NodeAddress = initiator.into(); // A session may already have been established. if self.sessions.get(&initiator).is_some() { trace!("Session already established with initiator: {}", initiator); return Ok(()); } - // If we haven't already sent a WhoAreYou, possibly punching the same hole using another - // relay, spawn a WHOAREYOU event to punch a hole in our NAT for initiator. - if self.active_challenges.get(&initiator).is_none() { - let whoareyou_ref = WhoAreYouRef(initiator, nonce); - if let Err(e) = self - .service_send - .send(HandlerOut::WhoAreYou(whoareyou_ref)) - .await - { - return Err(HolePunchError::TargetError(format!( - "Failed to send WhoAreYou to the service {}", - e - ))); - } - } else { + // Possibly, an attempt to punch this hole, using another relay, is in progress. + if self.active_challenges.get(&initiator).is_some() { trace!("WHOAREYOU packet already sent to initiator: {}", initiator); + return Ok(()); + } + // If not hole punch attempts are in progress, spawn a WHOAREYOU event to punch a hole in + // our NAT for initiator. + let whoareyou_ref = WhoAreYouRef(initiator, nonce); + if let Err(e) = self + .service_send + .send(HandlerOut::WhoAreYou(whoareyou_ref)) + .await + { + return Err(HolePunchError::TargetError(e.into())); } Ok(()) } diff --git a/src/node_info.rs b/src/node_info.rs index 01a64879e..862f8c4e8 100644 --- a/src/node_info.rs +++ b/src/node_info.rs @@ -1,8 +1,7 @@ use super::*; use crate::Enr; use enr::{CombinedPublicKey, NodeId}; -use rlp::{DecoderError, Rlp, RlpStream}; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::net::SocketAddr; #[cfg(feature = "libp2p")] use libp2p_core::{identity::PublicKey, multiaddr::Protocol, multihash, Multiaddr}; @@ -155,6 +154,32 @@ pub struct NodeAddress { pub node_id: NodeId, } +impl From for NodeAddress { + fn from(n: nat_hole_punch::NodeAddress) -> Self { + let nat_hole_punch::NodeAddress { + socket_addr, + node_id, + } = n; + Self { + socket_addr, + node_id, + } + } +} + +impl Into for NodeAddress { + fn into(self) -> nat_hole_punch::NodeAddress { + let NodeAddress { + socket_addr, + node_id, + } = self; + nat_hole_punch::NodeAddress { + socket_addr, + node_id, + } + } +} + impl Ord for NodeAddress { fn cmp(&self, other: &Self) -> std::cmp::Ordering { let ord = self.node_id.raw().cmp(&other.node_id.raw()); From 9b8f821410447be3250154d5c02988f784e53de0 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 23 Mar 2023 11:37:16 +0100 Subject: [PATCH 006/154] fixup! Plug relaying into service --- src/service.rs | 7 +++---- src/service/test.rs | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/service.rs b/src/service.rs index 1d2243ad0..4b8b3f7d6 100644 --- a/src/service.rs +++ b/src/service.rs @@ -357,6 +357,8 @@ impl Service { match event { HandlerOut::Established(enr, socket_addr, direction) => { self.send_event(Discv5Event::SessionEstablished(enr.clone(), socket_addr)); + // Remove potential relay that may be stored for peer. + self.new_peer_latest_relay.remove(&enr.node_id()); self.inject_session_established(enr, direction); } HandlerOut::Request(node_address, request) => { @@ -649,9 +651,6 @@ impl Service { let node_id = node_address.node_id; - // Peer is not behind a NAT. Remove potential relay that may be stored for peer. - self.new_peer_latest_relay.remove(&node_id); - match response.body { ResponseBody::Nodes { total, mut nodes } => { if total > MAX_NODES_RESPONSES as u64 { @@ -1174,7 +1173,7 @@ impl Service { /// Processes discovered peers from a query. fn discovered(&mut self, source: &NodeAddress, mut enrs: Vec, query_id: Option) { let local_id = self.local_enr.read().node_id(); - let discovered = enrs.retain(|enr| { + enrs.retain(|enr| { if enr.node_id() == local_id { return false; } diff --git a/src/service/test.rs b/src/service/test.rs index e986eb615..23e602ef0 100644 --- a/src/service/test.rs +++ b/src/service/test.rs @@ -48,7 +48,7 @@ async fn build_service( .executor(Box::::default()) .build(); // build the session service - let (_handler_exit, handler_send, handler_recv) = Handler::spawn::

( + let (_handler_exit, handler_send, handler_recv) = Handler::

::spawn( local_enr.clone(), enr_key.clone(), listen_socket, @@ -94,6 +94,7 @@ async fn build_service( event_stream: None, exit, config, + new_peer_latest_relay: Default::default(), } } From f53f3f66cd81270b66281ad20193a5d8feb4263d Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 23 Mar 2023 11:41:31 +0100 Subject: [PATCH 007/154] fixup! Plug-in hole punching --- src/handler/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/handler/tests.rs b/src/handler/tests.rs index 120927861..d23cf3cb4 100644 --- a/src/handler/tests.rs +++ b/src/handler/tests.rs @@ -48,7 +48,7 @@ async fn simple_session_message() { .build(&key2) .unwrap(); - let (_exit_send, sender_send, _sender_recv) = Handler::spawn::( + let (_exit_send, sender_send, _sender_recv) = Handler::::spawn( arc_rw!(sender_enr.clone()), arc_rw!(key1), sender_enr.udp4_socket().unwrap().into(), @@ -57,7 +57,7 @@ async fn simple_session_message() { .await .unwrap(); - let (_exit_recv, recv_send, mut receiver_recv) = Handler::spawn::( + let (_exit_recv, recv_send, mut receiver_recv) = Handler::::spawn( arc_rw!(receiver_enr.clone()), arc_rw!(key2), receiver_enr.udp4_socket().unwrap().into(), @@ -125,7 +125,7 @@ async fn multiple_messages() { .unwrap(); let (_exit_send, sender_handler, mut sender_handler_recv) = - Handler::spawn::( + Handler::::spawn( arc_rw!(sender_enr.clone()), arc_rw!(key1), sender_enr.udp4_socket().unwrap().into(), @@ -134,7 +134,7 @@ async fn multiple_messages() { .await .unwrap(); - let (_exit_recv, recv_send, mut receiver_handler) = Handler::spawn::( + let (_exit_recv, recv_send, mut receiver_handler) = Handler::::spawn( arc_rw!(receiver_enr.clone()), arc_rw!(key2), receiver_enr.udp4_socket().unwrap().into(), From 172d120abd7f5a26f9049c24ed218c01a5785070 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 25 Mar 2023 07:43:40 +0100 Subject: [PATCH 008/154] Update trait impl --- src/handler/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index b1d7f6045..8870fdf7e 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1224,7 +1224,7 @@ impl Handler

{ } } -#[async_trait] +#[async_trait(?Send)] impl NatHolePunch for Handler

{ type TNodeAddress = NodeAddress; type TDiscv5Error = Discv5Error; From 398710830f546614120cc62dfa9ff40569c92343 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 25 Mar 2023 15:42:03 +0100 Subject: [PATCH 009/154] Consolidate hole punching in Handler --- src/handler/mod.rs | 98 +++++++++++++++++++++++++++++---------------- src/service.rs | 85 ++++++--------------------------------- src/service/test.rs | 1 - 3 files changed, 76 insertions(+), 108 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 8870fdf7e..2824628f2 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -34,7 +34,7 @@ use crate::{ rpc::{Message, Request, RequestBody, RequestId, Response, ResponseBody}, socket, socket::{FilterConfig, Socket}, - Enr, + Enr, IpMode, }; use async_trait::async_trait; use delay_map::HashMapDelay; @@ -105,13 +105,6 @@ pub enum HandlerIn { /// The `WhoAreYouRef` is sent out in the `HandlerOut::WhoAreYou` event and should /// be returned here to submit the application's response. WhoAreYou(WhoAreYouRef, Option), - - /// The node address of the relay, the local node address, the message nonce of the timed out - /// request and the node address of the target peer to hole punch and a rleay init - /// notification. A FINDNODE request to a peer discovered in a find node query has timed out. - /// The target of that request may be behind a NAT. The peer which passed us the target in a - /// NODES response, is used as relay to attmept punching a hole in the targets NAT. - HolePunch(NodeAddress, NodeAddress, MessageNonce, NodeAddress), } /// Messages sent between a node on the network and `Handler`. @@ -136,10 +129,8 @@ pub enum HandlerOut { /// An RPC request failed. /// - /// This returns the request ID, an error indicating why the request failed and possibly also - /// the message nonce to allow for attempting NAT hole punching (attempted upon request time - /// out). - RequestFailed(RequestId, RequestError, Option), + /// This returns the request ID, an error indicating why the request failed. + RequestFailed(RequestId, RequestError), } /// How we connected to the node. @@ -212,7 +203,14 @@ pub struct Handler { socket: Socket, /// Exit channel to shutdown the handler. exit: oneshot::Receiver<()>, + /// Access generic when implementing traits for Handler. _phantom: PhantomData

, + /// The last peer to send us a new peer in a NODES response is stored as the new peer's + /// potential relay until the first request to the new peer after its discovery is either + /// responded or failed. + new_peer_latest_relay: HashMap, + /// Ip mode as set in config. + ip_mode: IpMode, } type HandlerReturn = ( @@ -264,6 +262,8 @@ impl Handler

{ // Attempt to bind to the socket before spinning up the send/recv tasks. let socket = Socket::new::

(socket_config).await?; + let ip_mode = config.ip_mode; + config .executor .clone() @@ -288,6 +288,8 @@ impl Handler

{ socket, exit, _phantom: PhantomData, + new_peer_latest_relay: Default::default(), + ip_mode, }; debug!("Handler Starting"); handler.start().await; @@ -308,22 +310,13 @@ impl Handler

{ let Request { id, body: request } = *request; if let Err(request_error) = self.send_request(contact, HandlerReqId::External(id.clone()), request).await { // If the sending failed report to the application - if let Err(e) = self.service_send.send(HandlerOut::RequestFailed(id, request_error, None)).await { + if let Err(e) = self.service_send.send(HandlerOut::RequestFailed(id, request_error)).await { warn!("Failed to inform that request failed {}", e) } } } HandlerIn::Response(dst, response) => self.send_response(dst, *response).await, HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge(wru_ref, enr).await, - HandlerIn::HolePunch(relay, local_node_address, - message_nonce, - target_node_address) => { - if let Err(e) = self.on_time_out(relay, local_node_address, - message_nonce, - target_node_address).await { - warn!("Failed to start hole punching. Error: {:?}", e); - } - } } } Some(inbound_packet) = self.socket.recv.recv() => { @@ -444,6 +437,31 @@ impl Handler

{ ) { if request_call.retries() >= self.request_retries { trace!("Request timed out with {}", node_address); + if let Some(relay) = self.new_peer_latest_relay.remove(&node_address.node_id) { + // The request might be timing out because the peer is behind a NAT. If we + // have a relay to the peer, attempt NAT hole punching. + let target = request_call.contact().node_address(); + let (socket_addr, node_id) = { + let local_enr = self.enr.read(); + let socket_addr = self.ip_mode.get_contactable_addr(&local_enr); + let node_id = local_enr.node_id(); + (socket_addr, node_id) + }; + if let Some(socket_addr) = socket_addr { + let local_node_address = NodeAddress { + socket_addr, + node_id, + }; + let nonce = request_call.packet().header.message_nonce; + if let Err(e) = self + .on_time_out(relay, local_node_address.into(), nonce, target.into()) + .await + { + warn!("Failed to start hole punching. Error: {:?}", e); + } + return; + } + } // Remove the request from the awaiting packet_filter self.remove_expected_response(node_address.socket_addr); // The request has timed out. We keep any established session for future use. @@ -477,7 +495,8 @@ impl Handler

{ return Err(RequestError::SelfRequest); } - // If there is already an active request or an active challenge (WHOAREYOU sent) for this node, add to pending requests + // If there is already an active request or an active challenge (WHOAREYOU sent) for this + // node, add to pending requests if self.active_requests.get(&node_address).is_some() || self.active_challenges.get(&node_address).is_some() { @@ -819,6 +838,7 @@ impl Handler

{ warn!("Failed to inform of established session {}", e) } self.new_session(node_address.clone(), session); + self.new_peer_latest_relay.remove(&node_address.node_id); self.handle_message( node_address.clone(), message_nonce, @@ -896,7 +916,7 @@ impl Handler

{ HandlerReqId::External(id) => { if let Err(e) = self .service_send - .send(HandlerOut::RequestFailed(id, request_error, None)) + .send(HandlerOut::RequestFailed(id, request_error)) .await { warn!("Failed to inform that request failed {}", e); @@ -1056,7 +1076,19 @@ impl Handler

{ // Check to see if this is a Nodes response, in which case we may require to wait for // extra responses - if let ResponseBody::Nodes { total, .. } = response.body { + if let ResponseBody::Nodes { total, ref nodes } = response.body { + for node in nodes { + if let Some(socket_addr) = self.ip_mode.get_contactable_addr(node) { + let node_id = node.node_id(); + let node_address = NodeAddress { + socket_addr, + node_id, + }; + if self.sessions.get(&node_address).is_none() { + self.new_peer_latest_relay.insert(node_id, node_address); + } + } + } if total > 1 { // This is a multi-response Nodes response if let Some(remaining_responses) = request_call.remaining_responses_mut() { @@ -1150,19 +1182,15 @@ impl Handler

{ HandlerReqId::External(id) => { if let Err(e) = self .service_send - .send(HandlerOut::RequestFailed( - id.clone(), - error.clone(), - Some(request_call.packet().header.message_nonce), - )) + .send(HandlerOut::RequestFailed(id.clone(), error.clone())) .await { warn!("Failed to inform request failure {}", e) } } } - let node_address = request_call.contact().node_address(); + self.new_peer_latest_relay.remove(&node_address.node_id); self.fail_session(&node_address, error, remove_session) .await; } @@ -1189,7 +1217,7 @@ impl Handler

{ HandlerReqId::External(id) => { if let Err(e) = self .service_send - .send(HandlerOut::RequestFailed(id, error.clone(), None)) + .send(HandlerOut::RequestFailed(id, error.clone())) .await { warn!("Failed to inform request failure {}", e) @@ -1224,7 +1252,7 @@ impl Handler

{ } } -#[async_trait(?Send)] +#[async_trait] impl NatHolePunch for Handler

{ type TNodeAddress = NodeAddress; type TDiscv5Error = Discv5Error; @@ -1232,7 +1260,7 @@ impl NatHolePunch for Handler

{ &mut self, relay: Self::TNodeAddress, local_node_address: Self::TNodeAddress, - timed_out_message_nonce: MessageNonce, + message_nonce: MessageNonce, target_node_address: Self::TNodeAddress, ) -> Result<(), HolePunchError> { // Another hole punch process may have just completed. @@ -1242,7 +1270,7 @@ impl NatHolePunch for Handler

{ if let Some(session) = self.sessions.get_mut(&relay) { let relay_init_notif = RelayInit( local_node_address.into(), - timed_out_message_nonce, + message_nonce, target_node_address.into(), ); // Encrypt the message and send diff --git a/src/service.rs b/src/service.rs index 4b8b3f7d6..354a3b0c0 100644 --- a/src/service.rs +++ b/src/service.rs @@ -25,7 +25,7 @@ use crate::{ NodeStatus, UpdateResult, MAX_NODES_PER_BUCKET, }, node_info::{NodeAddress, NodeContact, NonContactable}, - packet::{MessageNonce, ProtocolIdentity, MAX_PACKET_SIZE}, + packet::{ProtocolIdentity, MAX_PACKET_SIZE}, query_pool::{ FindNodeQueryConfig, PredicateQueryConfig, QueryId, QueryPool, QueryPoolState, TargetKey, }, @@ -199,11 +199,6 @@ pub struct Service { /// A channel that the service emits events on. event_stream: Option>, - - /// The last peer to send us a new peer in a NODES response is stored as the new peer's - /// potential relay until the first request to the new peer after its discovery is either - /// responded or failed. - new_peer_latest_relay: HashMap, } /// Active RPC request awaiting a response from the handler. @@ -301,7 +296,6 @@ impl Service { event_stream: None, exit, config: config.clone(), - new_peer_latest_relay: HashMap::default(), }; info!("Discv5 Service started"); @@ -357,8 +351,6 @@ impl Service { match event { HandlerOut::Established(enr, socket_addr, direction) => { self.send_event(Discv5Event::SessionEstablished(enr.clone(), socket_addr)); - // Remove potential relay that may be stored for peer. - self.new_peer_latest_relay.remove(&enr.node_id()); self.inject_session_established(enr, direction); } HandlerOut::Request(node_address, request) => { @@ -381,13 +373,13 @@ impl Service { } } } - HandlerOut::RequestFailed(request_id, error, message_nonce) => { + HandlerOut::RequestFailed(request_id, error) => { if let RequestError::Timeout = error { debug!("RPC Request timed out. id: {}", request_id); } else { warn!("RPC Request failed: id: {}, error {:?}", request_id, error); } - self.rpc_failure(request_id, error, message_nonce); + self.rpc_failure(request_id, error); } } } @@ -703,9 +695,7 @@ impl Service { node_address ); let ban_timeout = self.config.ban_duration.map(|v| Instant::now() + v); - PERMIT_BAN_LIST - .write() - .ban(node_address.clone(), ban_timeout); + PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); nodes.retain(|enr| { peer_key.log2_distance(&enr.node_id().into()).is_none() }); @@ -726,9 +716,7 @@ impl Service { active_request.contact ); let ban_timeout = self.config.ban_duration.map(|v| Instant::now() + v); - PERMIT_BAN_LIST - .write() - .ban(node_address.clone(), ban_timeout); + PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); } } @@ -779,7 +767,7 @@ impl Service { // ensure any mapping is removed in this rare case self.active_nodes_responses.remove(&node_id); - self.discovered(&node_address, nodes, active_request.query_id); + self.discovered(&node_id, nodes, active_request.query_id); } ResponseBody::Pong { enr_seq, ip, port } => { let socket = SocketAddr::new(ip, port); @@ -1171,7 +1159,7 @@ impl Service { } /// Processes discovered peers from a query. - fn discovered(&mut self, source: &NodeAddress, mut enrs: Vec, query_id: Option) { + fn discovered(&mut self, source: &NodeId, mut enrs: Vec, query_id: Option) { let local_id = self.local_enr.read().node_id(); enrs.retain(|enr| { if enr.node_id() == local_id { @@ -1186,8 +1174,7 @@ impl Service { // ignore peers that don't pass the table filter if (self.config.table_filter)(enr) { - let node_id = enr.node_id(); - let key = kbucket::Key::from(node_id); + let key = kbucket::Key::from(enr.node_id()); // If the ENR exists in the routing table and the discovered ENR has a greater // sequence number, perform some filter checks before updating the enr. @@ -1195,10 +1182,7 @@ impl Service { let must_update_enr = match self.kbuckets.write().entry(&key) { kbucket::Entry::Present(entry, _) => entry.value().seq() < enr.seq(), kbucket::Entry::Pending(mut entry, _) => entry.value().seq() < enr.seq(), - _ => { - self.new_peer_latest_relay.insert(node_id, source.clone()); - false - } + _ => false, }; if must_update_enr { @@ -1222,7 +1206,7 @@ impl Service { // requesting the target of the query, this ENR could be the result of requesting the // target-nodes own id. We don't want to add this as a "new" discovered peer in the // query, so we remove it from the discovered list here. - source.node_id != enr.node_id() + source != &enr.node_id() }); // if this is part of a query, update the query @@ -1241,7 +1225,7 @@ impl Service { peer_count += 1; } debug!("{} peers found for query id {:?}", peer_count, query_id); - query.on_success(&source.node_id, &enrs) + query.on_success(source, &enrs) } else { debug!("Response returned for ended query {:?}", query_id) } @@ -1380,12 +1364,7 @@ impl Service { /// A session could not be established or an RPC request timed-out (after a few retries, if /// specified). - fn rpc_failure( - &mut self, - id: RequestId, - error: RequestError, - message_nonce: Option, - ) { + fn rpc_failure(&mut self, id: RequestId, error: RequestError) { trace!("RPC Error removing request. Reason: {:?}, id {}", error, id); if let Some(active_request) = self.active_requests.remove(&id) { // If this is initiated by the user, return an error on the callback. All callbacks @@ -1414,44 +1393,6 @@ impl Service { // if a failed FindNodes request, ensure we haven't partially received packets. If // so, process the partially found nodes RequestBody::FindNode { .. } => { - let relay = self.new_peer_latest_relay.remove(&node_id); - if let RequestError::Timeout = error { - // The request might be timing out because the peer is behind a NAT. If we - // have a relay to the peer, attempt hole punching. - if let (Some(relay), Some(message_nonce)) = (relay, message_nonce) { - let target_node_address = active_request.contact.node_address(); - let local_enr = self.local_enr.read(); - let socket = match target_node_address.socket_addr { - SocketAddr::V4(_) => match local_enr.udp4_socket() { - Some(socket) => Some(SocketAddr::V4(socket)), - None => local_enr.udp6_socket().map(SocketAddr::V6), - }, - SocketAddr::V6(_) => match local_enr.udp6_socket() { - Some(socket) => Some(SocketAddr::V6(socket)), - None => local_enr.udp4_socket().map(SocketAddr::V4), - }, - }; - if let Some(socket_addr) = socket { - let local_node_address = NodeAddress { - socket_addr, - node_id: local_enr.node_id(), - }; - match self.handler_send.send(HandlerIn::HolePunch( - relay, - local_node_address, - message_nonce, - target_node_address, - )) { - Err(e) => { - warn!("Failed to start hole punch {}", e); - } - Ok(()) => { - return; - } - } - } - } - } if let Some(nodes_response) = self.active_nodes_responses.remove(&node_id) { if !nodes_response.received_nodes.is_empty() { warn!( @@ -1461,7 +1402,7 @@ impl Service { // if it's a query mark it as success, to process the partial // collection of peers self.discovered( - &active_request.contact.node_address(), + &node_id, nodes_response.received_nodes, active_request.query_id, ); diff --git a/src/service/test.rs b/src/service/test.rs index 23e602ef0..249329cb6 100644 --- a/src/service/test.rs +++ b/src/service/test.rs @@ -94,7 +94,6 @@ async fn build_service( event_stream: None, exit, config, - new_peer_latest_relay: Default::default(), } } From 410201ae29550e5d269a86392be2284cede1c5b2 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 25 Mar 2023 15:51:31 +0100 Subject: [PATCH 010/154] fixup! Consolidate hole punching in Handler --- src/handler/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 2824628f2..8fd56dab7 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1084,7 +1084,7 @@ impl Handler

{ socket_addr, node_id, }; - if self.sessions.get(&node_address).is_none() { + if self.sessions.peek(&node_address).is_none() { self.new_peer_latest_relay.insert(node_id, node_address); } } From 396a595a2995b6f68d0fbffc894037cda4e557c4 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 25 Mar 2023 16:09:16 +0100 Subject: [PATCH 011/154] fixup! Consolidate hole punching in Handler --- src/handler/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 8fd56dab7..6bc24b439 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1080,12 +1080,13 @@ impl Handler

{ for node in nodes { if let Some(socket_addr) = self.ip_mode.get_contactable_addr(node) { let node_id = node.node_id(); - let node_address = NodeAddress { + let new_peer_node_address = NodeAddress { socket_addr, node_id, }; - if self.sessions.peek(&node_address).is_none() { - self.new_peer_latest_relay.insert(node_id, node_address); + if self.sessions.peek(&new_peer_node_address).is_none() { + self.new_peer_latest_relay + .insert(node_id, node_address.clone()); } } } From 32b812eb036a994273e608de00e8ba7eee211deb Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 25 Mar 2023 16:27:45 +0100 Subject: [PATCH 012/154] Add trace info --- src/handler/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 6bc24b439..d3ddf50e3 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -441,6 +441,11 @@ impl Handler

{ // The request might be timing out because the peer is behind a NAT. If we // have a relay to the peer, attempt NAT hole punching. let target = request_call.contact().node_address(); + trace!( + "Trying to hole punch target {} with relay {:?}", + target, + relay + ); let (socket_addr, node_id) = { let local_enr = self.enr.read(); let socket_addr = self.ip_mode.get_contactable_addr(&local_enr); @@ -1274,6 +1279,11 @@ impl NatHolePunch for Handler

{ message_nonce, target_node_address.into(), ); + trace!( + "Sending realy init notif {:?} to relay {:?}", + relay_init_notif, + relay + ); // Encrypt the message and send let packet = match session.encrypt_message::

(self.node_id, &relay_init_notif.rlp_encode()) { From 5a2bbc88f89cbb06664e2b2195a5d8aa39595d9f Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 25 Mar 2023 16:56:29 +0100 Subject: [PATCH 013/154] fixup! Consolidate hole punching in Handler --- src/handler/mod.rs | 30 ++++++++++++++++-------------- src/handler/session.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index d3ddf50e3..ef483eb50 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1285,13 +1285,14 @@ impl NatHolePunch for Handler

{ relay ); // Encrypt the message and send - let packet = - match session.encrypt_message::

(self.node_id, &relay_init_notif.rlp_encode()) { - Ok(packet) => packet, - Err(e) => { - return Err(HolePunchError::RelayError(e)); - } - }; + let packet = match session + .encrypt_notification::

(self.node_id, &relay_init_notif.rlp_encode()) + { + Ok(packet) => packet, + Err(e) => { + return Err(HolePunchError::RelayError(e)); + } + }; self.send(relay, packet).await; } else { // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays @@ -1349,13 +1350,14 @@ impl NatHolePunch for Handler

{ // response recently. if let Some(session) = self.sessions.get_mut(&target) { // Encrypt the notification and send (encrypted same way as a message) - let packet = - match session.encrypt_message::

(self.node_id, ¬if_for_target.rlp_encode()) { - Ok(packet) => packet, - Err(e) => { - return Err(HolePunchError::RelayError(e)); - } - }; + let packet = match session + .encrypt_notification::

(self.node_id, ¬if_for_target.rlp_encode()) + { + Ok(packet) => packet, + Err(e) => { + return Err(HolePunchError::RelayError(e)); + } + }; self.send(target, packet).await; Ok(()) } else { diff --git a/src/handler/session.rs b/src/handler/session.rs index fedeb031d..f175be663 100644 --- a/src/handler/session.rs +++ b/src/handler/session.rs @@ -96,6 +96,48 @@ impl Session { }) } + /// Uses the current `Session` to encrypt a notification. Encrypt packets with the current + /// session key if we are awaiting a response from AuthMessage. Same as `encrypt_message` + /// apart form the the [`PacketHeader`]. + pub(crate) fn encrypt_notification( + &mut self, + src_id: NodeId, + message: &[u8], + ) -> Result { + self.counter += 1; + + // If the message nonce length is ever set below 4 bytes this will explode. The packet + // size constants shouldn't be modified. + let random_nonce: [u8; MESSAGE_NONCE_LENGTH - 4] = rand::random(); + let mut message_nonce: MessageNonce = [0u8; MESSAGE_NONCE_LENGTH]; + message_nonce[..4].copy_from_slice(&self.counter.to_be_bytes()); + message_nonce[4..].copy_from_slice(&random_nonce); + + // the authenticated data is the IV concatenated with the packet header + let iv: u128 = rand::random(); + let header = PacketHeader { + message_nonce, + kind: PacketKind::Notification { src_id }, + }; + + let mut authenticated_data = iv.to_be_bytes().to_vec(); + authenticated_data.extend_from_slice(&header.encode::

()); + + let cipher = crypto::encrypt_message( + &self.keys.encryption_key, + message_nonce, + message, + &authenticated_data, + )?; + + // construct a packet from the header and the cipher text + Ok(Packet { + iv, + header, + message: cipher, + }) + } + /// Decrypts an encrypted message. If a Session is already established, the original decryption /// keys are tried first, upon failure, the new keys are attempted. If the new keys succeed, /// the session keys are updated along with the Session state. From 84b05612ef9f9cf9080973199a10b8b15eab472e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 26 Mar 2023 10:04:17 +0200 Subject: [PATCH 014/154] Reinsert active request on hole punch attempt --- src/handler/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index ef483eb50..11e6c243b 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -464,6 +464,7 @@ impl Handler

{ { warn!("Failed to start hole punching. Error: {:?}", e); } + self.active_requests.insert(node_address, request_call); return; } } From 85575f5e6eb9a350b858fcf7aa947c51ee5cd6a7 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 27 Mar 2023 11:32:50 +0200 Subject: [PATCH 015/154] Update node address conversion --- src/node_info.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node_info.rs b/src/node_info.rs index 862f8c4e8..1db0261ad 100644 --- a/src/node_info.rs +++ b/src/node_info.rs @@ -162,7 +162,7 @@ impl From for NodeAddress { } = n; Self { socket_addr, - node_id, + node_id: NodeId::new(&node_id), } } } @@ -175,7 +175,7 @@ impl Into for NodeAddress { } = self; nat_hole_punch::NodeAddress { socket_addr, - node_id, + node_id: node_id.raw(), } } } From f6872cd66d963e8aacbeb23f83b5d229a35f5a92 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 28 Mar 2023 16:17:51 +0200 Subject: [PATCH 016/154] RO access kbuckets from handler for hole punch target peer lookup --- Cargo.toml | 2 +- src/error.rs | 1 + src/handler/mod.rs | 166 ++++++++++++++++++++++++++++----------------- src/kbucket.rs | 11 +++ src/node_info.rs | 26 ------- src/service.rs | 1 + 6 files changed, 119 insertions(+), 88 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 90d50ed74..9bf9cfd9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ categories = ["network-programming", "asynchronous"] exclude = [".gitignore", ".github/*"] [dependencies] -nat_hole_punch = { package = "nat_hole_punch", git = "https://github.com/emhane/nat_hole_punch", branch = "nat-hole-punch" } +nat_hole_punch = { package = "nat_hole_punch", git = "https://github.com/emhane/nat_hole_punch", branch = "standardise-nat-hole-punch" } enr = { version = "0.7.0", features = ["k256", "ed25519"] } tokio = { version = "1.15.0", features = ["net", "sync", "macros", "rt"] } tokio-stream = "0.1.8" diff --git a/src/error.rs b/src/error.rs index 788aa579a..8c2786e2a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -58,6 +58,7 @@ macro_rules! impl_from_to_variant { } impl_from_to_variant!(, tokio::sync::mpsc::error::SendError, Discv5Error, Self::ServiceChannelClosed); +impl_from_to_variant!(, NonContactable, Discv5Error, Self::InvalidEnr); #[derive(Debug, Clone, PartialEq, Eq)] /// Types of packet errors. diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 11e6c243b..217f0d51c 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -30,6 +30,8 @@ use crate::{ config::Discv5Config, discv5::PERMIT_BAN_LIST, error::{Discv5Error, RequestError}, + kbucket, + kbucket::KBucketsTable, packet::{ChallengeData, IdNonce, MessageNonce, Packet, PacketKind, ProtocolIdentity}, rpc::{Message, Request, RequestBody, RequestId, Response, ResponseBody}, socket, @@ -205,12 +207,8 @@ pub struct Handler { exit: oneshot::Receiver<()>, /// Access generic when implementing traits for Handler. _phantom: PhantomData

, - /// The last peer to send us a new peer in a NODES response is stored as the new peer's - /// potential relay until the first request to the new peer after its discovery is either - /// responded or failed. - new_peer_latest_relay: HashMap, - /// Ip mode as set in config. - ip_mode: IpMode, + /// Types necessary to plug in nat hole punching. + nat_hole_puncher: NatHolePuncher, } type HandlerReturn = ( @@ -224,6 +222,7 @@ impl Handler

{ pub async fn spawn( enr: Arc>, key: Arc>, + kbuckets: Arc>>, listen_socket: SocketAddr, config: Discv5Config, ) -> Result { @@ -262,7 +261,11 @@ impl Handler

{ // Attempt to bind to the socket before spinning up the send/recv tasks. let socket = Socket::new::

(socket_config).await?; - let ip_mode = config.ip_mode; + let nat_hole_puncher = NatHolePuncher { + new_peer_latest_relay: Default::default(), + ip_mode: config.ip_mode, + kbuckets, + }; config .executor @@ -288,8 +291,7 @@ impl Handler

{ socket, exit, _phantom: PhantomData, - new_peer_latest_relay: Default::default(), - ip_mode, + nat_hole_puncher, }; debug!("Handler Starting"); handler.start().await; @@ -437,7 +439,11 @@ impl Handler

{ ) { if request_call.retries() >= self.request_retries { trace!("Request timed out with {}", node_address); - if let Some(relay) = self.new_peer_latest_relay.remove(&node_address.node_id) { + if let Some(relay) = self + .nat_hole_puncher + .new_peer_latest_relay + .remove(&node_address.node_id) + { // The request might be timing out because the peer is behind a NAT. If we // have a relay to the peer, attempt NAT hole punching. let target = request_call.contact().node_address(); @@ -446,27 +452,16 @@ impl Handler

{ target, relay ); - let (socket_addr, node_id) = { - let local_enr = self.enr.read(); - let socket_addr = self.ip_mode.get_contactable_addr(&local_enr); - let node_id = local_enr.node_id(); - (socket_addr, node_id) - }; - if let Some(socket_addr) = socket_addr { - let local_node_address = NodeAddress { - socket_addr, - node_id, - }; - let nonce = request_call.packet().header.message_nonce; - if let Err(e) = self - .on_time_out(relay, local_node_address.into(), nonce, target.into()) - .await - { - warn!("Failed to start hole punching. Error: {:?}", e); - } - self.active_requests.insert(node_address, request_call); - return; + let local_enr = self.enr.read().clone(); + let nonce = request_call.packet().header.message_nonce; + if let Err(e) = self + .on_time_out(relay, local_enr, nonce, target.into()) + .await + { + warn!("Failed to start hole punching. Error: {:?}", e); } + self.active_requests.insert(node_address, request_call); + return; } // Remove the request from the awaiting packet_filter self.remove_expected_response(node_address.socket_addr); @@ -844,7 +839,9 @@ impl Handler

{ warn!("Failed to inform of established session {}", e) } self.new_session(node_address.clone(), session); - self.new_peer_latest_relay.remove(&node_address.node_id); + self.nat_hole_puncher + .new_peer_latest_relay + .remove(&node_address.node_id); self.handle_message( node_address.clone(), message_nonce, @@ -1084,14 +1081,17 @@ impl Handler

{ // extra responses if let ResponseBody::Nodes { total, ref nodes } = response.body { for node in nodes { - if let Some(socket_addr) = self.ip_mode.get_contactable_addr(node) { + if let Some(socket_addr) = + self.nat_hole_puncher.ip_mode.get_contactable_addr(node) + { let node_id = node.node_id(); let new_peer_node_address = NodeAddress { socket_addr, node_id, }; if self.sessions.peek(&new_peer_node_address).is_none() { - self.new_peer_latest_relay + self.nat_hole_puncher + .new_peer_latest_relay .insert(node_id, node_address.clone()); } } @@ -1197,7 +1197,9 @@ impl Handler

{ } } let node_address = request_call.contact().node_address(); - self.new_peer_latest_relay.remove(&node_address.node_id); + self.nat_hole_puncher + .new_peer_latest_relay + .remove(&node_address.node_id); self.fail_session(&node_address, error, remove_session) .await; } @@ -1261,24 +1263,25 @@ impl Handler

{ #[async_trait] impl NatHolePunch for Handler

{ + type TEnr = Enr; type TNodeAddress = NodeAddress; type TDiscv5Error = Discv5Error; async fn on_time_out( &mut self, relay: Self::TNodeAddress, - local_node_address: Self::TNodeAddress, - message_nonce: MessageNonce, - target_node_address: Self::TNodeAddress, + local_enr: Self::TEnr, + timed_out_message_nonce: MessageNonce, + target_session_index: Self::TNodeAddress, ) -> Result<(), HolePunchError> { - // Another hole punch process may have just completed. - if self.sessions.get(&target_node_address).is_some() { + // Another hole punch process with this target may have just completed. + if self.sessions.get(&target_session_index).is_some() { return Ok(()); } if let Some(session) = self.sessions.get_mut(&relay) { let relay_init_notif = RelayInit( - local_node_address.into(), - message_nonce, - target_node_address.into(), + local_enr, + target_session_index.node_id.raw(), + timed_out_message_nonce, ); trace!( "Sending realy init notif {:?} to relay {:?}", @@ -1310,7 +1313,7 @@ impl NatHolePunch for Handler

{ async fn handle_decryption_with_session( &mut self, - session_index: Self::TNodeAddress, + session_index: Self::TNodeAddress, // notif sender notif_nonce: MessageNonce, notif: &[u8], authenticated_data: &[u8], @@ -1340,17 +1343,30 @@ impl NatHolePunch for Handler

{ async fn on_relay_init( &mut self, - relay_init_notif: RelayInit, + notif: RelayInit, ) -> Result<(), HolePunchError> { - let RelayInit(initiator, nonce, target) = relay_init_notif; + let RelayInit(initiator, tgt, nonce) = notif; let notif_for_target = RelayMsg(initiator, nonce); - // Overshadow with local version of same type - let target: NodeAddress = target.into(); - // Check for an established session. Only relay to peers we have sessions with, - // otherwise it is unlikely we have passed the target node to the initiator in a NODES - // response recently. - if let Some(session) = self.sessions.get_mut(&target) { - // Encrypt the notification and send (encrypted same way as a message) + + // Check for target peer in our kbuckets otherwise drop notification. + let tgt_node_id = NodeId::new(&tgt); + let key = kbucket::Key::from(tgt_node_id); + let tgt_enr = match self.nat_hole_puncher.kbuckets.read().get_enr(&key) { + Some(enr) => enr.clone(), + None => { + return Err(HolePunchError::RelayError(Discv5Error::Custom( + "Target peer not in kbuckets", + ))) + } + }; + + let tgt_node_address = + match NodeContact::try_from_enr(tgt_enr, self.nat_hole_puncher.ip_mode) { + Ok(contact) => contact.node_address(), + Err(e) => return Err(HolePunchError::RelayError(e.into())), + }; + if let Some(session) = self.sessions.get_mut(&tgt_node_address) { + // Encrypt the notification and send let packet = match session .encrypt_notification::

(self.node_id, ¬if_for_target.rlp_encode()) { @@ -1359,7 +1375,7 @@ impl NatHolePunch for Handler

{ return Err(HolePunchError::RelayError(e)); } }; - self.send(target, packet).await; + self.send(tgt_node_address, packet).await; Ok(()) } else { // Either the session is being established or has expired. We simply drop the @@ -1375,24 +1391,39 @@ impl NatHolePunch for Handler

{ async fn on_relay_msg( &mut self, - notif: RelayMsg, + notif: RelayMsg, ) -> Result<(), HolePunchError> { let RelayMsg(initiator, nonce) = notif; - // Overshadow with local version of same type - let initiator: NodeAddress = initiator.into(); + + let initiator_node_address = + match NodeContact::try_from_enr(initiator, self.nat_hole_puncher.ip_mode) { + Ok(contact) => contact.node_address(), + Err(e) => return Err(HolePunchError::TargetError(e.into())), + }; + // A session may already have been established. - if self.sessions.get(&initiator).is_some() { - trace!("Session already established with initiator: {}", initiator); + if self.sessions.get(&initiator_node_address).is_some() { + trace!( + "Session already established with initiator: {}", + initiator_node_address + ); return Ok(()); } // Possibly, an attempt to punch this hole, using another relay, is in progress. - if self.active_challenges.get(&initiator).is_some() { - trace!("WHOAREYOU packet already sent to initiator: {}", initiator); + if self + .active_challenges + .get(&initiator_node_address) + .is_some() + { + trace!( + "WHOAREYOU packet already sent to initiator: {}", + initiator_node_address + ); return Ok(()); } // If not hole punch attempts are in progress, spawn a WHOAREYOU event to punch a hole in // our NAT for initiator. - let whoareyou_ref = WhoAreYouRef(initiator, nonce); + let whoareyou_ref = WhoAreYouRef(initiator_node_address, nonce); if let Err(e) = self .service_send .send(HandlerOut::WhoAreYou(whoareyou_ref)) @@ -1403,3 +1434,16 @@ impl NatHolePunch for Handler

{ Ok(()) } } + +/// Types necessary implement trait [`NatHolePunch`]. +struct NatHolePuncher { + /// The last peer to send us a new peer in a NODES response is stored as the new peer's + /// potential relay until the first request to the new peer after its discovery is either + /// responded or failed. + new_peer_latest_relay: HashMap, + /// Ip mode as set in config. + ip_mode: IpMode, + /// The KBuckets. Handler should only be read the kbuckets! Holding write locks may change + /// flow too much and this has not been examined. + kbuckets: Arc>>, +} diff --git a/src/kbucket.rs b/src/kbucket.rs index 67d137c6a..63ee08cdf 100644 --- a/src/kbucket.rs +++ b/src/kbucket.rs @@ -718,6 +718,17 @@ where let index = BucketIndex::new(&self.local_key.distance(key)); index.map(|i| i.get()) } + + /// Returns an enr without applying pending. Use with care. + pub fn get_enr<'a>(&'a self, key: &Key) -> Option<&'a TVal> { + let index = BucketIndex::new(&self.local_key.distance(key)); + if let Some(i) = index { + let bucket = &self.buckets[i.get()]; + bucket.get(key).map(|entry| &entry.value) + } else { + None + } + } } /// An iterator over (some projection of) the closest entries in a diff --git a/src/node_info.rs b/src/node_info.rs index 1db0261ad..1b299deb2 100644 --- a/src/node_info.rs +++ b/src/node_info.rs @@ -154,32 +154,6 @@ pub struct NodeAddress { pub node_id: NodeId, } -impl From for NodeAddress { - fn from(n: nat_hole_punch::NodeAddress) -> Self { - let nat_hole_punch::NodeAddress { - socket_addr, - node_id, - } = n; - Self { - socket_addr, - node_id: NodeId::new(&node_id), - } - } -} - -impl Into for NodeAddress { - fn into(self) -> nat_hole_punch::NodeAddress { - let NodeAddress { - socket_addr, - node_id, - } = self; - nat_hole_punch::NodeAddress { - socket_addr, - node_id: node_id.raw(), - } - } -} - impl Ord for NodeAddress { fn cmp(&self, other: &Self) -> std::cmp::Ordering { let ord = self.node_id.raw().cmp(&other.node_id.raw()); diff --git a/src/service.rs b/src/service.rs index 354a3b0c0..afec49000 100644 --- a/src/service.rs +++ b/src/service.rs @@ -266,6 +266,7 @@ impl Service { let (handler_exit, handler_send, handler_recv) = Handler::

::spawn( local_enr.clone(), enr_key.clone(), + kbuckets.clone(), listen_socket, config.clone(), ) From 867fc04bf30070ca3365c90286319bd45f90e688 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 29 Mar 2023 12:04:29 +0200 Subject: [PATCH 017/154] Replace RO access to kbuckets with channels --- src/handler/mod.rs | 65 ++++++++++++++++++++++++++++++++++++---------- src/kbucket.rs | 11 -------- src/service.rs | 12 ++++++++- 3 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 217f0d51c..01a8070d3 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -30,8 +30,6 @@ use crate::{ config::Discv5Config, discv5::PERMIT_BAN_LIST, error::{Discv5Error, RequestError}, - kbucket, - kbucket::KBucketsTable, packet::{ChallengeData, IdNonce, MessageNonce, Packet, PacketKind, ProtocolIdentity}, rpc::{Message, Request, RequestBody, RequestId, Response, ResponseBody}, socket, @@ -107,6 +105,9 @@ pub enum HandlerIn { /// The `WhoAreYouRef` is sent out in the `HandlerOut::WhoAreYou` event and should /// be returned here to submit the application's response. WhoAreYou(WhoAreYouRef, Option), + + /// Response to a [`HandlerOut::FindEnrForHolePunch`]. Returns the ENR if it was found. + HolePunchEnr(NodeId, Option), } /// Messages sent between a node on the network and `Handler`. @@ -133,6 +134,11 @@ pub enum HandlerOut { /// /// This returns the request ID, an error indicating why the request failed. RequestFailed(RequestId, RequestError), + + /// A peer has supposed we have passed it another peer in a NODES response, if that is true + /// (very probably not false) the ENR of that peer is returned in a + /// [`HandlerIn::HolePunchEnr`]. + FindHolePunchEnr(NodeId), } /// How we connected to the node. @@ -222,7 +228,6 @@ impl Handler

{ pub async fn spawn( enr: Arc>, key: Arc>, - kbuckets: Arc>>, listen_socket: SocketAddr, config: Discv5Config, ) -> Result { @@ -264,7 +269,7 @@ impl Handler

{ let nat_hole_puncher = NatHolePuncher { new_peer_latest_relay: Default::default(), ip_mode: config.ip_mode, - kbuckets, + tgt_enr_tx: Default::default(), }; config @@ -319,6 +324,17 @@ impl Handler

{ } HandlerIn::Response(dst, response) => self.send_response(dst, *response).await, HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge(wru_ref, enr).await, + HandlerIn::HolePunchEnr(tgt_node_id, tgt_enr) => { + let Some(relay_processes) = self.nat_hole_puncher.tgt_enr_tx.remove(&tgt_node_id) else { + debug!("Relay processes have already received the target enr"); + continue + }; + for tx in relay_processes { + if let Err(e) = tx.send(tgt_enr.clone()) { + error!("Failed to send to relay process, relay process will now be parked forever. error: {:?}", e); + } + } + } } } Some(inbound_packet) = self.socket.recv.recv() => { @@ -1330,12 +1346,12 @@ impl NatHolePunch for Handler

{ // packet. This means we drop the current session and the notification. self.fail_session(&session_index, RequestError::InvalidRemotePacket, true) .await; - return Err(HolePunchError::SessionError(e)); + return Err(HolePunchError::InitiatorError(e)); } Ok(bytes) => Ok(bytes), } } - None => Err(HolePunchError::SessionError( + None => Err(HolePunchError::InitiatorError( Discv5Error::SessionNotEstablished, )), } @@ -1346,14 +1362,34 @@ impl NatHolePunch for Handler

{ notif: RelayInit, ) -> Result<(), HolePunchError> { let RelayInit(initiator, tgt, nonce) = notif; - let notif_for_target = RelayMsg(initiator, nonce); // Check for target peer in our kbuckets otherwise drop notification. let tgt_node_id = NodeId::new(&tgt); - let key = kbucket::Key::from(tgt_node_id); - let tgt_enr = match self.nat_hole_puncher.kbuckets.read().get_enr(&key) { - Some(enr) => enr.clone(), - None => { + let (tx, rx) = oneshot::channel::>(); + + let waiting_relay_processes = self + .nat_hole_puncher + .tgt_enr_tx + .entry(tgt_node_id) + .or_default(); + waiting_relay_processes.push(tx); + drop(waiting_relay_processes); + + if let Err(e) = self + .service_send + .send(HandlerOut::FindHolePunchEnr(tgt_node_id)) + .await + { + return Err(HolePunchError::RelayError(e.into())); + } + let tgt_enr = match rx.await { + Ok(Some(enr)) => enr, + Err(_) => { + return Err(HolePunchError::RelayError(Discv5Error::Custom( + "Relay process failed to receive target enr", + ))) + } + Ok(None) => { return Err(HolePunchError::RelayError(Discv5Error::Custom( "Target peer not in kbuckets", ))) @@ -1366,6 +1402,8 @@ impl NatHolePunch for Handler

{ Err(e) => return Err(HolePunchError::RelayError(e.into())), }; if let Some(session) = self.sessions.get_mut(&tgt_node_address) { + // Assemble the notification for the target + let notif_for_target = RelayMsg(initiator, nonce); // Encrypt the notification and send let packet = match session .encrypt_notification::

(self.node_id, ¬if_for_target.rlp_encode()) @@ -1443,7 +1481,6 @@ struct NatHolePuncher { new_peer_latest_relay: HashMap, /// Ip mode as set in config. ip_mode: IpMode, - /// The KBuckets. Handler should only be read the kbuckets! Holding write locks may change - /// flow too much and this has not been examined. - kbuckets: Arc>>, + /// Channels to processes waiting on the ENR of the target of a [`RelayInit`]. + tgt_enr_tx: HashMap>>>, } diff --git a/src/kbucket.rs b/src/kbucket.rs index 63ee08cdf..67d137c6a 100644 --- a/src/kbucket.rs +++ b/src/kbucket.rs @@ -718,17 +718,6 @@ where let index = BucketIndex::new(&self.local_key.distance(key)); index.map(|i| i.get()) } - - /// Returns an enr without applying pending. Use with care. - pub fn get_enr<'a>(&'a self, key: &Key) -> Option<&'a TVal> { - let index = BucketIndex::new(&self.local_key.distance(key)); - if let Some(i) = index { - let bucket = &self.buckets[i.get()]; - bucket.get(key).map(|entry| &entry.value) - } else { - None - } - } } /// An iterator over (some projection of) the closest entries in a diff --git a/src/service.rs b/src/service.rs index afec49000..160e42b36 100644 --- a/src/service.rs +++ b/src/service.rs @@ -266,7 +266,6 @@ impl Service { let (handler_exit, handler_send, handler_recv) = Handler::

::spawn( local_enr.clone(), enr_key.clone(), - kbuckets.clone(), listen_socket, config.clone(), ) @@ -382,6 +381,17 @@ impl Service { } self.rpc_failure(request_id, error); } + HandlerOut::FindHolePunchEnr(tgt_node_id) => { + // check if we know this node id in our routing table + let key = kbucket::Key::from(tgt_node_id); + let tgt_enr = match self.kbuckets.write().entry(&key) { + kbucket::Entry::Present(entry, _) => Some(entry.value().clone()), + _ => None + }; + if let Err(e) = self.handler_send.send(HandlerIn::HolePunchEnr(tgt_node_id, tgt_enr)) { + warn!("Failed to send target enr to relay proccess, error: {}", e); + } + } } } event = Service::bucket_maintenance_poll(&self.kbuckets) => { From 8ef3df7814142c121d2a41756660cd20c7c7ad55 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 29 Mar 2023 13:34:28 +0200 Subject: [PATCH 018/154] Add trace messages --- src/handler/mod.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 01a8070d3..3af02a64d 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1300,9 +1300,9 @@ impl NatHolePunch for Handler

{ timed_out_message_nonce, ); trace!( - "Sending realy init notif {:?} to relay {:?}", + "Sending notif to relay {}. relay init: {}", + relay.node_id, relay_init_notif, - relay ); // Encrypt the message and send let packet = match session @@ -1403,10 +1403,15 @@ impl NatHolePunch for Handler

{ }; if let Some(session) = self.sessions.get_mut(&tgt_node_address) { // Assemble the notification for the target - let notif_for_target = RelayMsg(initiator, nonce); + let relay_msg_notif = RelayMsg(initiator, nonce); + trace!( + "Sending notif to target {}. relay msg: {}", + tgt_node_id, + relay_msg_notif, + ); // Encrypt the notification and send let packet = match session - .encrypt_notification::

(self.node_id, ¬if_for_target.rlp_encode()) + .encrypt_notification::

(self.node_id, &relay_msg_notif.rlp_encode()) { Ok(packet) => packet, Err(e) => { From 8b033674ce37927ea5794e49d3145cac7ea9e4ac Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 29 Mar 2023 13:49:51 +0200 Subject: [PATCH 019/154] fixup! Add trace messages --- src/handler/mod.rs | 5 ++++- src/service.rs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 3af02a64d..81a1c60a8 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -330,8 +330,9 @@ impl Handler

{ continue }; for tx in relay_processes { + trace!("Sending tgt enr to relay process"); if let Err(e) = tx.send(tgt_enr.clone()) { - error!("Failed to send to relay process, relay process will now be parked forever. error: {:?}", e); + error!("Failed to send to relay process, relay process will now be parked forever. Error: {:?}", e); } } } @@ -1375,6 +1376,7 @@ impl NatHolePunch for Handler

{ waiting_relay_processes.push(tx); drop(waiting_relay_processes); + if let Err(e) = self .service_send .send(HandlerOut::FindHolePunchEnr(tgt_node_id)) @@ -1395,6 +1397,7 @@ impl NatHolePunch for Handler

{ ))) } }; + trace!("Enr of target {} was found in kbuckets", tgt_node_id); let tgt_node_address = match NodeContact::try_from_enr(tgt_enr, self.nat_hole_puncher.ip_mode) { diff --git a/src/service.rs b/src/service.rs index 160e42b36..73a5df6e3 100644 --- a/src/service.rs +++ b/src/service.rs @@ -388,6 +388,7 @@ impl Service { kbucket::Entry::Present(entry, _) => Some(entry.value().clone()), _ => None }; + trace!("Sending hole punch target enr to handler {:?}", tgt_enr); if let Err(e) = self.handler_send.send(HandlerIn::HolePunchEnr(tgt_node_id, tgt_enr)) { warn!("Failed to send target enr to relay proccess, error: {}", e); } From 2075d5ee4002a4c33ff575d4b0bc501111f03c61 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 29 Mar 2023 14:21:00 +0200 Subject: [PATCH 020/154] Handle each realy init separately --- src/handler/mod.rs | 128 ++++++++++++++++++--------------------------- src/node_info.rs | 2 +- src/service.rs | 4 +- 3 files changed, 55 insertions(+), 79 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 81a1c60a8..4714d62b8 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -76,7 +76,7 @@ use session::Session; const BANNED_NODES_CHECK: u64 = 300; // Check every 5 minutes. /// Messages sent from the application layer to `Handler`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] #[allow(clippy::large_enum_variant)] pub enum HandlerIn { /// A Request to send to a `NodeContact` has been received from the application layer. A @@ -107,7 +107,7 @@ pub enum HandlerIn { WhoAreYou(WhoAreYouRef, Option), /// Response to a [`HandlerOut::FindEnrForHolePunch`]. Returns the ENR if it was found. - HolePunchEnr(NodeId, Option), + HolePunchEnr(Option, RelayMsg), } /// Messages sent between a node on the network and `Handler`. @@ -138,7 +138,7 @@ pub enum HandlerOut { /// A peer has supposed we have passed it another peer in a NODES response, if that is true /// (very probably not false) the ENR of that peer is returned in a /// [`HandlerIn::HolePunchEnr`]. - FindHolePunchEnr(NodeId), + FindHolePunchEnr(NodeId, RelayMsg), } /// How we connected to the node. @@ -269,7 +269,6 @@ impl Handler

{ let nat_hole_puncher = NatHolePuncher { new_peer_latest_relay: Default::default(), ip_mode: config.ip_mode, - tgt_enr_tx: Default::default(), }; config @@ -324,16 +323,9 @@ impl Handler

{ } HandlerIn::Response(dst, response) => self.send_response(dst, *response).await, HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge(wru_ref, enr).await, - HandlerIn::HolePunchEnr(tgt_node_id, tgt_enr) => { - let Some(relay_processes) = self.nat_hole_puncher.tgt_enr_tx.remove(&tgt_node_id) else { - debug!("Relay processes have already received the target enr"); - continue - }; - for tx in relay_processes { - trace!("Sending tgt enr to relay process"); - if let Err(e) = tx.send(tgt_enr.clone()) { - error!("Failed to send to relay process, relay process will now be parked forever. Error: {:?}", e); - } + HandlerIn::HolePunchEnr(tgt_enr, relay_msg_notif) => { + if let Err(e) = self.on_hole_punch_tgt_enr(tgt_enr, relay_msg_notif).await { + warn!("Failed to realy. Error: {}", e); } } } @@ -1276,6 +1268,48 @@ impl Handler

{ .ban_nodes .retain(|_, time| time.is_none() || Some(Instant::now()) < *time); } + + async fn on_hole_punch_tgt_enr( + &mut self, + tgt_enr: Option, + relay_msg_notif: RelayMsg, + ) -> Result<(), HolePunchError> { + let Some(tgt_enr) = tgt_enr else { + return Err(HolePunchError::RelayError(Discv5Error::Custom("Target enr not found"))); + }; + let tgt_node_address = + match NodeContact::try_from_enr(tgt_enr, self.nat_hole_puncher.ip_mode) { + Ok(contact) => contact.node_address(), + Err(e) => return Err(HolePunchError::RelayError(e.into())), + }; + if let Some(session) = self.sessions.get_mut(&tgt_node_address) { + trace!( + "Sending notif to target {}. relay msg: {}", + tgt_node_address.node_id, + relay_msg_notif, + ); + // Encrypt the notification and send + let packet = match session + .encrypt_notification::

(self.node_id, &relay_msg_notif.rlp_encode()) + { + Ok(packet) => packet, + Err(e) => { + return Err(HolePunchError::RelayError(e)); + } + }; + self.send(tgt_node_address, packet).await; + Ok(()) + } else { + // Either the session is being established or has expired. We simply drop the + // notification in this case to ensure hole punch round-trip time stays within the + // time out of the udp entrypoint for the target peer in the intiator's router, set by + // the original timed out FINDNODE request from the initiator, as the initiator may + // also be behind a NAT. + Err(HolePunchError::RelayError( + Discv5Error::SessionNotEstablished, + )) + } + } } #[async_trait] @@ -1363,76 +1397,20 @@ impl NatHolePunch for Handler

{ notif: RelayInit, ) -> Result<(), HolePunchError> { let RelayInit(initiator, tgt, nonce) = notif; + // Assemble the notification for the target + let relay_msg_notif = RelayMsg(initiator, nonce); // Check for target peer in our kbuckets otherwise drop notification. let tgt_node_id = NodeId::new(&tgt); - let (tx, rx) = oneshot::channel::>(); - - let waiting_relay_processes = self - .nat_hole_puncher - .tgt_enr_tx - .entry(tgt_node_id) - .or_default(); - waiting_relay_processes.push(tx); - drop(waiting_relay_processes); - if let Err(e) = self .service_send - .send(HandlerOut::FindHolePunchEnr(tgt_node_id)) + .send(HandlerOut::FindHolePunchEnr(tgt_node_id, relay_msg_notif)) .await { return Err(HolePunchError::RelayError(e.into())); } - let tgt_enr = match rx.await { - Ok(Some(enr)) => enr, - Err(_) => { - return Err(HolePunchError::RelayError(Discv5Error::Custom( - "Relay process failed to receive target enr", - ))) - } - Ok(None) => { - return Err(HolePunchError::RelayError(Discv5Error::Custom( - "Target peer not in kbuckets", - ))) - } - }; - trace!("Enr of target {} was found in kbuckets", tgt_node_id); - - let tgt_node_address = - match NodeContact::try_from_enr(tgt_enr, self.nat_hole_puncher.ip_mode) { - Ok(contact) => contact.node_address(), - Err(e) => return Err(HolePunchError::RelayError(e.into())), - }; - if let Some(session) = self.sessions.get_mut(&tgt_node_address) { - // Assemble the notification for the target - let relay_msg_notif = RelayMsg(initiator, nonce); - trace!( - "Sending notif to target {}. relay msg: {}", - tgt_node_id, - relay_msg_notif, - ); - // Encrypt the notification and send - let packet = match session - .encrypt_notification::

(self.node_id, &relay_msg_notif.rlp_encode()) - { - Ok(packet) => packet, - Err(e) => { - return Err(HolePunchError::RelayError(e)); - } - }; - self.send(tgt_node_address, packet).await; - Ok(()) - } else { - // Either the session is being established or has expired. We simply drop the - // notification in this case to ensure hole punch round-trip time stays within the - // time out of the udp entrypoint for the target peer in the intiator's router, set by - // the original timed out FINDNODE request from the initiator, as the initiator may - // also be behind a NAT. - Err(HolePunchError::RelayError( - Discv5Error::SessionNotEstablished, - )) - } + Ok(()) } async fn on_relay_msg( @@ -1489,6 +1467,4 @@ struct NatHolePuncher { new_peer_latest_relay: HashMap, /// Ip mode as set in config. ip_mode: IpMode, - /// Channels to processes waiting on the ENR of the target of a [`RelayInit`]. - tgt_enr_tx: HashMap>>>, } diff --git a/src/node_info.rs b/src/node_info.rs index 1b299deb2..7d5b8014f 100644 --- a/src/node_info.rs +++ b/src/node_info.rs @@ -8,7 +8,7 @@ use libp2p_core::{identity::PublicKey, multiaddr::Protocol, multihash, Multiaddr /// This type relaxes the requirement of having an ENR to connect to a node, to allow for unsigned /// connection types, such as multiaddrs. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct NodeContact { /// Key to use for communications with this node. public_key: CombinedPublicKey, diff --git a/src/service.rs b/src/service.rs index 73a5df6e3..ece40b565 100644 --- a/src/service.rs +++ b/src/service.rs @@ -381,7 +381,7 @@ impl Service { } self.rpc_failure(request_id, error); } - HandlerOut::FindHolePunchEnr(tgt_node_id) => { + HandlerOut::FindHolePunchEnr(tgt_node_id, relay_msg_notif) => { // check if we know this node id in our routing table let key = kbucket::Key::from(tgt_node_id); let tgt_enr = match self.kbuckets.write().entry(&key) { @@ -389,7 +389,7 @@ impl Service { _ => None }; trace!("Sending hole punch target enr to handler {:?}", tgt_enr); - if let Err(e) = self.handler_send.send(HandlerIn::HolePunchEnr(tgt_node_id, tgt_enr)) { + if let Err(e) = self.handler_send.send(HandlerIn::HolePunchEnr(tgt_enr, relay_msg_notif)) { warn!("Failed to send target enr to relay proccess, error: {}", e); } } From a8223e654c1d563536449a197e1cb0beb71410c1 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 31 Mar 2023 13:35:14 +0200 Subject: [PATCH 021/154] Receive responses in notification packets --- src/handler/mod.rs | 2 +- src/handler/session.rs | 2 +- src/packet/mod.rs | 14 +++++++------- src/service.rs | 1 - 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 4714d62b8..153e6f4cc 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -399,7 +399,7 @@ impl Handler

{ ) .await } - PacketKind::Notification { src_id } => { + PacketKind::SessionMessage { src_id } => { let node_address = NodeAddress { socket_addr: inbound_packet.src_address, node_id: src_id, diff --git a/src/handler/session.rs b/src/handler/session.rs index f175be663..9c0d74436 100644 --- a/src/handler/session.rs +++ b/src/handler/session.rs @@ -117,7 +117,7 @@ impl Session { let iv: u128 = rand::random(); let header = PacketHeader { message_nonce, - kind: PacketKind::Notification { src_id }, + kind: PacketKind::SessionMessage { src_id }, }; let mut authenticated_data = iv.to_be_bytes().to_vec(); diff --git a/src/packet/mod.rs b/src/packet/mod.rs index 4299e36db..e30edd78c 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -143,7 +143,7 @@ pub enum PacketKind { }, /// A notification. Differs from [`PacketKind::Message`] in the way it handles sessions. If a /// session doesn't exist to decrypt or encrypt a notification, it is dropped. - Notification { + SessionMessage { /// The sending NodeId. src_id: NodeId, }, @@ -155,7 +155,7 @@ impl From<&PacketKind> for u8 { PacketKind::Message { .. } => 0, PacketKind::WhoAreYou { .. } => 1, PacketKind::Handshake { .. } => 2, - PacketKind::Notification { .. } => 3, + PacketKind::SessionMessage { .. } => 3, } } } @@ -164,7 +164,7 @@ impl PacketKind { /// Encodes the packet type into its corresponding auth_data. pub fn encode(&self) -> Vec { match self { - PacketKind::Message { src_id } | PacketKind::Notification { src_id } => { + PacketKind::Message { src_id } | PacketKind::SessionMessage { src_id } => { src_id.raw().to_vec() } PacketKind::WhoAreYou { id_nonce, enr_seq } => { @@ -290,7 +290,7 @@ impl PacketKind { } let src_id = NodeId::parse(auth_data).map_err(|_| PacketError::InvalidNodeId)?; - Ok(PacketKind::Notification { src_id }) + Ok(PacketKind::SessionMessage { src_id }) } _ => Err(PacketError::UnknownPacket), } @@ -383,7 +383,7 @@ impl Packet { PacketKind::WhoAreYou { .. } => true, PacketKind::Message { .. } | PacketKind::Handshake { .. } - | PacketKind::Notification { .. } => false, + | PacketKind::SessionMessage { .. } => false, } } @@ -391,7 +391,7 @@ impl Packet { /// src_id in this case. pub fn src_id(&self) -> Option { match self.header.kind { - PacketKind::Message { src_id } | PacketKind::Notification { src_id } => Some(src_id), + PacketKind::Message { src_id } | PacketKind::SessionMessage { src_id } => Some(src_id), PacketKind::WhoAreYou { .. } => None, PacketKind::Handshake { src_id, .. } => Some(src_id), } @@ -586,7 +586,7 @@ impl std::fmt::Display for PacketKind { hex::encode(ephem_pubkey), enr_record ), - PacketKind::Notification { src_id } => write!(f, "Notification {{ src_id: {src_id} }}"), + PacketKind::SessionMessage { src_id } => write!(f, "Notification {{ src_id: {src_id} }}"), } } } diff --git a/src/service.rs b/src/service.rs index ece40b565..dfcd41258 100644 --- a/src/service.rs +++ b/src/service.rs @@ -388,7 +388,6 @@ impl Service { kbucket::Entry::Present(entry, _) => Some(entry.value().clone()), _ => None }; - trace!("Sending hole punch target enr to handler {:?}", tgt_enr); if let Err(e) = self.handler_send.send(HandlerIn::HolePunchEnr(tgt_enr, relay_msg_notif)) { warn!("Failed to send target enr to relay proccess, error: {}", e); } From 8ac46181464fa43dca7f8e83a9b5acef09b58298 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 31 Mar 2023 16:45:23 +0200 Subject: [PATCH 022/154] Reset renaming of packet kind --- Cargo.toml | 2 +- src/handler/mod.rs | 293 ++++++++++++++++++++++------------------- src/handler/session.rs | 2 +- src/packet/mod.rs | 16 ++- 4 files changed, 170 insertions(+), 143 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9bf9cfd9b..b16c605dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ categories = ["network-programming", "asynchronous"] exclude = [".gitignore", ".github/*"] [dependencies] -nat_hole_punch = { package = "nat_hole_punch", git = "https://github.com/emhane/nat_hole_punch", branch = "standardise-nat-hole-punch" } +nat_hole_punch = { package = "nat_hole_punch", git = "https://github.com/emhane/nat_hole_punch", branch = "nat-hole-punch-discv5.2" } enr = { version = "0.7.0", features = ["k256", "ed25519"] } tokio = { version = "1.15.0", features = ["net", "sync", "macros", "rt"] } tokio-stream = "0.1.8" diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 153e6f4cc..050d1807a 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -399,23 +399,18 @@ impl Handler

{ ) .await } - PacketKind::SessionMessage { src_id } => { + PacketKind::Notification { src_id } => { let node_address = NodeAddress { socket_addr: inbound_packet.src_address, node_id: src_id, }; - trace!("Received notification from: {}", node_address); - if let Err(e) = self - .on_notification( - node_address, - message_nonce, - &inbound_packet.message, - &inbound_packet.authenticated_data, - ) - .await - { - warn!("Failed to process notification packet. Error: {:?}", e); - } + self.handle_notification( + node_address, + message_nonce, + &inbound_packet.message, + &inbound_packet.authenticated_data, + ) + .await } } } @@ -940,114 +935,114 @@ impl Handler

{ } } - /// Handle a standard message that does not contain an authentication header. - #[allow(clippy::single_match)] - async fn handle_message( + /// Handle a session message that is dropped if it can't be decrypted. + async fn handle_notification( &mut self, - node_address: NodeAddress, + node_address: NodeAddress, // session message sender message_nonce: MessageNonce, message: &[u8], authenticated_data: &[u8], ) { // check if we have an available session - if let Some(session) = self.sessions.get_mut(&node_address) { - // attempt to decrypt and process the message. - let message = match session.decrypt_message(message_nonce, message, authenticated_data) - { - Ok(m) => match Message::decode(&m) { - Ok(p) => p, - Err(e) => { - warn!("Failed to decode message. Error: {:?}, {}", e, node_address); + let Some(session) = self.sessions.get_mut(&node_address) else { + warn!( + "Dropping message. Error: {}, {}", + Discv5Error::SessionNotEstablished, + node_address + ); + return; + }; + // attempt to decrypt notification (same decryption as for a message) + let message = match session.decrypt_message(message_nonce, message, authenticated_data) { + Err(e) => { + // We have a session, but the session message could not be decrypted. It is + // likely the node sending this message has dropped their session. Since + // this is a session message that assumes an established session, we do + // not reply with a WHOAREYOU to this random packet. This means we drop + // the current session and the packet. + self.fail_session(&node_address, RequestError::InvalidRemotePacket, true) + .await; + warn!( + "Dropping message that should have been part of a session. Error: {}", + e + ); + return; + } + Ok(ref bytes) => match Message::decode(bytes) { + Ok(message) => message, + Err(msg_err) => { + if let Err(notif_err) = self.on_notification(bytes).await { + warn!( + "Failed to decode as message and notification. Error: {:?}, {}, {}", + msg_err, notif_err, node_address + ); return; } - }, - Err(e) => { - // We have a session, but the message could not be decrypted. It is likely the node - // sending this message has dropped their session. In this case, this message is a - // Random packet and we should reply with a WHOAREYOU. - // This means we need to drop the current session and re-establish. - trace!("Decryption failed. Error {}", e); - debug!( - "Message from node: {} is not encrypted with known session keys.", - node_address - ); - self.fail_session(&node_address, RequestError::InvalidRemotePacket, true) - .await; - // If we haven't already sent a WhoAreYou, - // spawn a WHOAREYOU event to check for highest known ENR - if self.active_challenges.get(&node_address).is_none() { - let whoareyou_ref = WhoAreYouRef(node_address, message_nonce); - if let Err(e) = self - .service_send - .send(HandlerOut::WhoAreYou(whoareyou_ref)) - .await - { - warn!("Failed to send WhoAreYou to the service {}", e) - } - } else { - trace!("WHOAREYOU packet already sent: {}", node_address); - } return; } - }; - - trace!("Received message from: {}", node_address); + }, + }; - // Remove any associated request from pending_request - match message { - Message::Request(request) => { - // report the request to the application - if let Err(e) = self - .service_send - .send(HandlerOut::Request(node_address, Box::new(request))) - .await - { - warn!("Failed to report request to application {}", e) - } - } - Message::Response(response) => { - // Sessions could be awaiting an ENR response. Check if this response matches - // these - if let Some(request_id) = session.awaiting_enr.as_ref() { - if &response.id == request_id { - session.awaiting_enr = None; - match response.body { - ResponseBody::Nodes { mut nodes, .. } => { - // Received the requested ENR - if let Some(enr) = nodes.pop() { - if self.verify_enr(&enr, &node_address) { - // Notify the application - // This can occur when we try to dial a node without an - // ENR. In this case we have attempted to establish the - // connection, so this is an outgoing connection. - if let Err(e) = self - .service_send - .send(HandlerOut::Established( - enr, - node_address.socket_addr, - ConnectionDirection::Outgoing, - )) - .await - { - warn!("Failed to inform established outgoing connection {}", e) - } - return; - } - } - } - _ => {} + match message { + Message::Response(response) => { + // Sessions could be awaiting an ENR response. Check if this response matches + // these + let Some(request_id) = session.awaiting_enr.as_ref() else { + return; + }; + if &response.id == request_id { + session.awaiting_enr = None; + if let ResponseBody::Nodes { mut nodes, .. } = response.body { + // Received the requested ENR + let Some(enr) = nodes.pop() else { + return; + }; + if self.verify_enr(&enr, &node_address) { + // Notify the application + // This can occur when we try to dial a node without an + // ENR. In this case we have attempted to establish the + // connection, so this is an outgoing connection. + if let Err(e) = self + .service_send + .send(HandlerOut::Established( + enr, + node_address.socket_addr, + ConnectionDirection::Outgoing, + )) + .await + { + warn!("Failed to inform established outgoing connection {}", e) } - debug!("Session failed invalid ENR response"); - self.fail_session(&node_address, RequestError::InvalidRemoteEnr, true) - .await; return; } } - // Handle standard responses - self.handle_response(node_address, response).await; + debug!("Session failed invalid ENR response"); + self.fail_session(&node_address, RequestError::InvalidRemoteEnr, true) + .await; + return; } + // Handle standard responses + self.handle_response(node_address, response).await; } - } else { + _ => { + warn!( + "Peer sent message type that shouldn't be sent in packet type Message, {}", + node_address + ); + } + } + } + + /// Handle a standard message that does not contain an authentication header. + async fn handle_message( + &mut self, + node_address: NodeAddress, + message_nonce: MessageNonce, + message: &[u8], + authenticated_data: &[u8], + ) { + // check if we have an available session + let Some(session) = self.sessions.get_mut(&node_address) else { // no session exists trace!("Received a message without a session. {}", node_address); trace!("Requesting a WHOAREYOU packet to be sent."); @@ -1063,6 +1058,66 @@ impl Handler

{ e ) } + return; + }; + // attempt to decrypt and process the message. + let message = match session.decrypt_message(message_nonce, message, authenticated_data) { + Ok(m) => match Message::decode(&m) { + Ok(p) => p, + Err(e) => { + warn!("Failed to decode message. Error: {:?}, {}", e, node_address); + return; + } + }, + Err(e) => { + // We have a session, but the message could not be decrypted. It is likely the node + // sending this message has dropped their session. In this case, this message is a + // Random packet and we should reply with a WHOAREYOU. + // This means we need to drop the current session and re-establish. + trace!("Decryption failed. Error {}", e); + debug!( + "Message from node: {} is not encrypted with known session keys.", + node_address + ); + self.fail_session(&node_address, RequestError::InvalidRemotePacket, true) + .await; + // If we haven't already sent a WhoAreYou, + // spawn a WHOAREYOU event to check for highest known ENR + if self.active_challenges.get(&node_address).is_none() { + let whoareyou_ref = WhoAreYouRef(node_address, message_nonce); + if let Err(e) = self + .service_send + .send(HandlerOut::WhoAreYou(whoareyou_ref)) + .await + { + warn!("Failed to send WhoAreYou to the service {}", e) + } + } else { + trace!("WHOAREYOU packet already sent: {}", node_address); + } + return; + } + }; + + trace!("Received message from: {}", node_address); + + match message { + Message::Request(request) => { + // report the request to the application + if let Err(e) = self + .service_send + .send(HandlerOut::Request(node_address, Box::new(request))) + .await + { + warn!("Failed to report request to application {}", e) + } + } + _ => { + warn!( + "Peer sent message type that shouldn't be sent in packet type Message, {}", + node_address + ); + } } } @@ -1362,36 +1417,6 @@ impl NatHolePunch for Handler

{ Ok(()) } - async fn handle_decryption_with_session( - &mut self, - session_index: Self::TNodeAddress, // notif sender - notif_nonce: MessageNonce, - notif: &[u8], - authenticated_data: &[u8], - ) -> Result, HolePunchError> { - // check if we have an available session - match self.sessions.get_mut(&session_index) { - Some(session) => { - // attempt to decrypt notification (same decryption as for a message) - match session.decrypt_message(notif_nonce, notif, authenticated_data) { - Err(e) => { - // We have a session, but the notification could not be decrypted. It is - // likely the node sending this message has dropped their session. Since - // this is a notification, we do not reply with a WHOAREYOU to this random - // packet. This means we drop the current session and the notification. - self.fail_session(&session_index, RequestError::InvalidRemotePacket, true) - .await; - return Err(HolePunchError::InitiatorError(e)); - } - Ok(bytes) => Ok(bytes), - } - } - None => Err(HolePunchError::InitiatorError( - Discv5Error::SessionNotEstablished, - )), - } - } - async fn on_relay_init( &mut self, notif: RelayInit, diff --git a/src/handler/session.rs b/src/handler/session.rs index 9c0d74436..f175be663 100644 --- a/src/handler/session.rs +++ b/src/handler/session.rs @@ -117,7 +117,7 @@ impl Session { let iv: u128 = rand::random(); let header = PacketHeader { message_nonce, - kind: PacketKind::SessionMessage { src_id }, + kind: PacketKind::Notification { src_id }, }; let mut authenticated_data = iv.to_be_bytes().to_vec(); diff --git a/src/packet/mod.rs b/src/packet/mod.rs index e30edd78c..4889bcec0 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -143,7 +143,7 @@ pub enum PacketKind { }, /// A notification. Differs from [`PacketKind::Message`] in the way it handles sessions. If a /// session doesn't exist to decrypt or encrypt a notification, it is dropped. - SessionMessage { + Notification { /// The sending NodeId. src_id: NodeId, }, @@ -155,7 +155,7 @@ impl From<&PacketKind> for u8 { PacketKind::Message { .. } => 0, PacketKind::WhoAreYou { .. } => 1, PacketKind::Handshake { .. } => 2, - PacketKind::SessionMessage { .. } => 3, + PacketKind::Notification { .. } => 3, } } } @@ -164,7 +164,7 @@ impl PacketKind { /// Encodes the packet type into its corresponding auth_data. pub fn encode(&self) -> Vec { match self { - PacketKind::Message { src_id } | PacketKind::SessionMessage { src_id } => { + PacketKind::Message { src_id } | PacketKind::Notification { src_id } => { src_id.raw().to_vec() } PacketKind::WhoAreYou { id_nonce, enr_seq } => { @@ -290,7 +290,7 @@ impl PacketKind { } let src_id = NodeId::parse(auth_data).map_err(|_| PacketError::InvalidNodeId)?; - Ok(PacketKind::SessionMessage { src_id }) + Ok(PacketKind::Notification { src_id }) } _ => Err(PacketError::UnknownPacket), } @@ -383,7 +383,7 @@ impl Packet { PacketKind::WhoAreYou { .. } => true, PacketKind::Message { .. } | PacketKind::Handshake { .. } - | PacketKind::SessionMessage { .. } => false, + | PacketKind::Notification { .. } => false, } } @@ -391,7 +391,7 @@ impl Packet { /// src_id in this case. pub fn src_id(&self) -> Option { match self.header.kind { - PacketKind::Message { src_id } | PacketKind::SessionMessage { src_id } => Some(src_id), + PacketKind::Message { src_id } | PacketKind::Notification { src_id } => Some(src_id), PacketKind::WhoAreYou { .. } => None, PacketKind::Handshake { src_id, .. } => Some(src_id), } @@ -586,7 +586,9 @@ impl std::fmt::Display for PacketKind { hex::encode(ephem_pubkey), enr_record ), - PacketKind::SessionMessage { src_id } => write!(f, "Notification {{ src_id: {src_id} }}"), + PacketKind::Notification { src_id } => { + write!(f, "Notification {{ src_id: {src_id} }}") + } } } } From 7e4f9df24dc262bd4344b11e50c2b46a92ca655d Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 31 Mar 2023 17:06:15 +0200 Subject: [PATCH 023/154] Send responses in notification --- src/handler/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 050d1807a..7b70c1844 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -563,7 +563,7 @@ impl Handler

{ // Check for an established session if let Some(session) = self.sessions.get_mut(&node_address) { // Encrypt the message and send - let packet = match session.encrypt_message::

(self.node_id, &response.encode()) { + let packet = match session.encrypt_notification::

(self.node_id, &response.encode()) { Ok(packet) => packet, Err(e) => { warn!("Could not encrypt response: {:?}", e); @@ -935,7 +935,7 @@ impl Handler

{ } } - /// Handle a session message that is dropped if it can't be decrypted. + /// Handle a notification packet, that is dropped if it can't be decrypted. async fn handle_notification( &mut self, node_address: NodeAddress, // session message sender @@ -971,6 +971,7 @@ impl Handler

{ Ok(ref bytes) => match Message::decode(bytes) { Ok(message) => message, Err(msg_err) => { + // try to decode the message as an application layer notification if let Err(notif_err) = self.on_notification(bytes).await { warn!( "Failed to decode as message and notification. Error: {:?}, {}, {}", From 7043e661b7a05e7c1b7720de72bd5d769e909553 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 31 Mar 2023 17:13:07 +0200 Subject: [PATCH 024/154] fixup! Reset renaming of packet kind --- src/packet/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/packet/mod.rs b/src/packet/mod.rs index 4889bcec0..ed81b85d8 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -141,8 +141,9 @@ pub enum PacketKind { /// The ENR record of the node if the WHOAREYOU request is out-dated. enr_record: Option, }, - /// A notification. Differs from [`PacketKind::Message`] in the way it handles sessions. If a - /// session doesn't exist to decrypt or encrypt a notification, it is dropped. + /// A notification. Differs from [`PacketKind::Message`] in the way it handles sessions. A + /// notification packet doesn't trigger a WHOAREYOU response. If a session doesn't exist to + /// decrypt or encrypt a notification, it is dropped. Notification { /// The sending NodeId. src_id: NodeId, From c6fcccbc0cedf65112dd45e2944a89be4db6dcd3 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 1 Apr 2023 09:20:14 +0200 Subject: [PATCH 025/154] fixup! Reinsert active request on hole punch attempt --- src/handler/mod.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 7b70c1844..f05574dae 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -458,14 +458,18 @@ impl Handler

{ ); let local_enr = self.enr.read().clone(); let nonce = request_call.packet().header.message_nonce; - if let Err(e) = self + match self .on_time_out(relay, local_enr, nonce, target.into()) .await { - warn!("Failed to start hole punching. Error: {:?}", e); + Err(e) => { + warn!("Failed to start hole punching. Error: {:?}", e); + } + Ok(()) => { + self.active_requests.insert(node_address, request_call); + return; + } } - self.active_requests.insert(node_address, request_call); - return; } // Remove the request from the awaiting packet_filter self.remove_expected_response(node_address.socket_addr); @@ -1358,7 +1362,7 @@ impl Handler

{ } else { // Either the session is being established or has expired. We simply drop the // notification in this case to ensure hole punch round-trip time stays within the - // time out of the udp entrypoint for the target peer in the intiator's router, set by + // time out of the udp entrypoint for the target peer in the initiator's NAT, set by // the original timed out FINDNODE request from the initiator, as the initiator may // also be behind a NAT. Err(HolePunchError::RelayError( @@ -1407,7 +1411,7 @@ impl NatHolePunch for Handler

{ self.send(relay, packet).await; } else { // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays - // within the time out of the udp entrypoint for the target peer in the intiator's + // within the time out of the udp entrypoint for the target peer in the initiator's // router, set by the original timed out FINDNODE request from the initiator, as the // initiator may also be behind a NAT. warn!( From 6906ab9712378697cfa8f8ecfd0241128bf6c34f Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 6 Apr 2023 13:43:42 +0200 Subject: [PATCH 026/154] Plug in mechanism for keeping hole punched for peer --- src/error.rs | 6 +++--- src/handler/mod.rs | 48 +++++++++++++++++++++++++++++++--------------- src/packet/mod.rs | 6 +++--- src/socket/mod.rs | 2 +- src/socket/recv.rs | 27 +++++++++++++++++++------- src/socket/send.rs | 32 +++++++++++++++++++++++-------- 6 files changed, 84 insertions(+), 37 deletions(-) diff --git a/src/error.rs b/src/error.rs index 8c2786e2a..49fd92ab1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -47,7 +47,7 @@ impl From for Discv5Error { } } -macro_rules! impl_from_to_variant { +macro_rules! impl_from_variant { ($(<$($generic: ident$(: $trait: path)*,)+>)*, $from_type: ty, $to_type: ty, $variant: path) => { impl$(<$($generic$(: $trait)*,)+>)* From<$from_type> for $to_type { fn from(_e: $from_type) -> Self { @@ -57,8 +57,8 @@ macro_rules! impl_from_to_variant { }; } -impl_from_to_variant!(, tokio::sync::mpsc::error::SendError, Discv5Error, Self::ServiceChannelClosed); -impl_from_to_variant!(, NonContactable, Discv5Error, Self::InvalidEnr); +impl_from_variant!(, tokio::sync::mpsc::error::SendError, Discv5Error, Self::ServiceChannelClosed); +impl_from_variant!(, NonContactable, Discv5Error, Self::InvalidEnr); #[derive(Debug, Clone, PartialEq, Eq)] /// Types of packet errors. diff --git a/src/handler/mod.rs b/src/handler/mod.rs index f05574dae..5ef853c13 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -33,11 +33,11 @@ use crate::{ packet::{ChallengeData, IdNonce, MessageNonce, Packet, PacketKind, ProtocolIdentity}, rpc::{Message, Request, RequestBody, RequestId, Response, ResponseBody}, socket, - socket::{FilterConfig, Socket}, + socket::{FilterConfig, Outbound, Socket}, Enr, IpMode, }; use async_trait::async_trait; -use delay_map::HashMapDelay; +use delay_map::{HashMapDelay, HashSetDelay}; use enr::{CombinedKey, NodeId}; use futures::prelude::*; use nat_hole_punch::*; @@ -269,6 +269,7 @@ impl Handler

{ let nat_hole_puncher = NatHolePuncher { new_peer_latest_relay: Default::default(), ip_mode: config.ip_mode, + hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), }; config @@ -341,6 +342,11 @@ impl Handler

{ // challenge. We process them here self.send_next_request(node_address).await; } + Some(Ok(node_address)) = self.nat_hole_puncher.hole_punch_tracker.next() => { + if let Err(e) = self.on_hole_punch_expired(node_address).await { + warn!("Failed to keep hole punched for peer, error: {}", e); + } + } _ = banned_nodes_check.tick() => self.unban_nodes_check(), // Unban nodes that are past the timeout _ = &mut self.exit => { return; @@ -483,7 +489,7 @@ impl Handler

{ request_call.body(), node_address ); - self.send(node_address.clone(), request_call.packet().clone()) + self.send(node_address.clone(), request_call.packet().clone().into()) .await; request_call.increment_retries(); self.active_requests.insert(node_address, request_call); @@ -556,7 +562,7 @@ impl Handler

{ ); // let the filter know we are expecting a response self.add_expected_response(node_address.socket_addr); - self.send(node_address.clone(), packet).await; + self.send(node_address.clone(), packet.into()).await; self.active_requests.insert(node_address, call); Ok(()) @@ -574,7 +580,7 @@ impl Handler

{ return; } }; - self.send(node_address, packet).await; + self.send(node_address, packet.into()).await; } else { // Either the session is being established or has expired. We simply drop the // response in this case. @@ -614,7 +620,7 @@ impl Handler

{ .expect("Must be the correct challenge size"); debug!("Sending WHOAREYOU to {}", node_address); self.add_expected_response(node_address.socket_addr); - self.send(node_address.clone(), packet).await; + self.send(node_address.clone(), packet.into()).await; self.active_challenges.insert( node_address, Challenge { @@ -743,7 +749,7 @@ impl Handler

{ // Reinsert the request_call self.insert_active_request(request_call); // Send the actual packet to the send task. - self.send(node_address.clone(), auth_packet).await; + self.send(node_address.clone(), auth_packet.into()).await; // Notify the application that the session has been established self.service_send @@ -765,7 +771,7 @@ impl Handler

{ request_call.set_handshake_sent(); // Reinsert the request_call self.insert_active_request(request_call); - self.send(node_address.clone(), auth_packet).await; + self.send(node_address.clone(), auth_packet.into()).await; let id = RequestId::random(); let request = RequestBody::FindNode { distances: vec![0] }; @@ -975,7 +981,7 @@ impl Handler

{ Ok(ref bytes) => match Message::decode(bytes) { Ok(message) => message, Err(msg_err) => { - // try to decode the message as an application layer notification + // try to decode the message as an application layer notification if let Err(notif_err) = self.on_notification(bytes).await { warn!( "Failed to decode as message and notification. Error: {:?}, {}, {}", @@ -1307,14 +1313,17 @@ impl Handler

{ } /// Sends a packet to the send handler to be encoded and sent. - async fn send(&mut self, node_address: NodeAddress, packet: Packet) { + async fn send(&mut self, node_address: NodeAddress, packet: Outbound) { let outbound_packet = socket::OutboundPacket { - node_address, + node_address: node_address.clone(), packet, }; if let Err(e) = self.socket.send.send(outbound_packet).await { warn!("Failed to send outbound packet {}", e) } + self.nat_hole_puncher + .hole_punch_tracker + .insert(node_address); } /// Check if any banned nodes have served their time and unban them. @@ -1357,7 +1366,7 @@ impl Handler

{ return Err(HolePunchError::RelayError(e)); } }; - self.send(tgt_node_address, packet).await; + self.send(tgt_node_address, packet.into()).await; Ok(()) } else { // Either the session is being established or has expired. We simply drop the @@ -1408,7 +1417,7 @@ impl NatHolePunch for Handler

{ return Err(HolePunchError::RelayError(e)); } }; - self.send(relay, packet).await; + self.send(relay, packet.into()).await; } else { // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays // within the time out of the udp entrypoint for the target peer in the initiator's @@ -1487,14 +1496,23 @@ impl NatHolePunch for Handler

{ } Ok(()) } + async fn on_hole_punch_expired( + &mut self, + dst: Self::TNodeAddress, + ) -> Result<(), HolePunchError> { + Ok(self.send(dst, Outbound::KeepHolePunched).await) + } } /// Types necessary implement trait [`NatHolePunch`]. struct NatHolePuncher { + /// Ip mode as set in config. + ip_mode: IpMode, /// The last peer to send us a new peer in a NODES response is stored as the new peer's /// potential relay until the first request to the new peer after its discovery is either /// responded or failed. new_peer_latest_relay: HashMap, - /// Ip mode as set in config. - ip_mode: IpMode, + /// Keeps track if this node needs to send a packet to a peer in order to keep a hole punched + /// for it in its NAT. + hole_punch_tracker: HashSetDelay, } diff --git a/src/packet/mod.rs b/src/packet/mod.rs index ed81b85d8..db819ee3d 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -141,9 +141,9 @@ pub enum PacketKind { /// The ENR record of the node if the WHOAREYOU request is out-dated. enr_record: Option, }, - /// A notification. Differs from [`PacketKind::Message`] in the way it handles sessions. A - /// notification packet doesn't trigger a WHOAREYOU response. If a session doesn't exist to - /// decrypt or encrypt a notification, it is dropped. + /// A notification. Differs from [`PacketKind::Message`] in the way it handles sessions. A + /// notification packet doesn't trigger a WHOAREYOU response. If a session doesn't exist to + /// decrypt or encrypt a notification, it is dropped. Notification { /// The sending NodeId. src_id: NodeId, diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 300440020..79ba527c5 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -21,7 +21,7 @@ pub use filter::{ FilterConfig, }; pub use recv::InboundPacket; -pub use send::OutboundPacket; +pub use send::{Outbound, OutboundPacket}; /// Convenience objects for setting up the recv handler. pub struct SocketConfig { diff --git a/src/socket/recv.rs b/src/socket/recv.rs index 11d81745b..d973e22e2 100644 --- a/src/socket/recv.rs +++ b/src/socket/recv.rs @@ -6,6 +6,7 @@ use super::filter::{Filter, FilterConfig}; use crate::{ ipmode::to_ipv4_mapped, metrics::METRICS, node_info::NodeAddress, packet::*, Executor, }; +use nat_hole_punch; use parking_lot::RwLock; use std::{ collections::HashMap, @@ -143,14 +144,26 @@ impl RecvHandler { return; } // Decodes the packet - let (packet, authenticated_data) = - match Packet::decode::

(&self.node_id, &self.recv_buffer[..length]) { - Ok(p) => p, - Err(e) => { - debug!("Packet decoding failed: {:?}", e); // could not decode the packet, drop it - return; + let (packet, authenticated_data) = match Packet::decode::

( + &self.node_id, + &self.recv_buffer[..length], + ) { + Ok(p) => p, + Err(e) => { + // This could be a packet to keep a NAT hole punched for this node in the + // sender's NAT, hence only serves purpose for the sender. + if nat_hole_punch::is_keep_hole_punched_packet(length) { + debug!("Appears to be a packet to keep a hole punched in sender's NAT, dropping. src: {}", src_address); + } else { + // Could not decode the packet, drop it. + debug!( + "Packet decoding failed, src: {}, error: {:?}", + src_address, e + ); } - }; + return; + } + }; // If this is not a challenge packet, we immediately know its src_id and so pass it // through the second filter. diff --git a/src/socket/send.rs b/src/socket/send.rs index 079be3c7a..14bff241a 100644 --- a/src/socket/send.rs +++ b/src/socket/send.rs @@ -1,5 +1,6 @@ //! This is a standalone task that encodes and sends Discv5 UDP packets use crate::{metrics::METRICS, node_info::NodeAddress, packet::*, Executor}; +use nat_hole_punch::impl_from_variant_wrap; use std::sync::Arc; use tokio::{ net::UdpSocket, @@ -7,11 +8,18 @@ use tokio::{ }; use tracing::{debug, trace, warn}; +pub enum Outbound { + Packet(Packet), + KeepHolePunched, +} + +impl_from_variant_wrap!(, Packet, Outbound, Self::Packet); + pub struct OutboundPacket { /// The destination node address pub node_address: NodeAddress, /// The packet to be encoded. - pub packet: Packet, + pub packet: Outbound, } /// The main task that handles outbound UDP packets. @@ -53,15 +61,23 @@ impl SendHandler { async fn start(&mut self) { loop { tokio::select! { - Some(packet) = self.handler_recv.recv() => { - let encoded_packet = packet.packet.encode::

(&packet.node_address.node_id); - if encoded_packet.len() > MAX_PACKET_SIZE { - warn!("Sending packet larger than max size: {} max: {}", encoded_packet.len(), MAX_PACKET_SIZE); - } - if let Err(e) = self.send.send_to(&encoded_packet, &packet.node_address.socket_addr).await { + Some(outbound) = self.handler_recv.recv() => { + let packet_bytes = match outbound.packet { + Outbound::Packet(packet) => { + let dst_id = outbound.node_address.node_id; + let encoded_packet = packet.encode::

(&dst_id); + if encoded_packet.len() > MAX_PACKET_SIZE { + warn!("Sending packet larger than max size: {} max: {}", encoded_packet.len(), MAX_PACKET_SIZE); + } + encoded_packet + } + Outbound::KeepHolePunched => vec![], + }; + let dst_addr = outbound.node_address.socket_addr; + if let Err(e) = self.send.send_to(&packet_bytes, &dst_addr).await { trace!("Could not send packet. Error: {:?}", e); } else { - METRICS.add_sent_bytes(encoded_packet.len()); + METRICS.add_sent_bytes(packet_bytes.len()); } } _ = &mut self.exit => { From a1c9754cf5ba72372ada0028a61ec9cdc93b2d99 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 6 Apr 2023 19:08:27 +0200 Subject: [PATCH 027/154] Only give work of keeping hole punched to nodes that aren't WAN reachable --- Cargo.toml | 2 + src/handler/mod.rs | 138 +++++++++++++++++++++++++++++++++++++++------ src/service.rs | 13 +++++ src/socket/mod.rs | 2 +- src/socket/send.rs | 32 +++++------ 5 files changed, 152 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b16c605dd..ea4207d1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,8 @@ hashlink = "0.7.0" delay_map = "0.3.0" more-asserts = "0.2.2" async-trait = "0.1.67" +tokio-context = "0.1.3" +mio = "0.8.6" [dev-dependencies] rand_07 = { package = "rand", version = "0.7" } diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 5ef853c13..5182cbe83 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -40,14 +40,16 @@ use async_trait::async_trait; use delay_map::{HashMapDelay, HashSetDelay}; use enr::{CombinedKey, NodeId}; use futures::prelude::*; +use mio::net::UdpSocket; use nat_hole_punch::*; use parking_lot::RwLock; +use rand::prelude::*; use std::{ collections::HashMap, convert::TryFrom, default::Default, marker::PhantomData, - net::SocketAddr, + net::{IpAddr, SocketAddr}, pin::Pin, sync::{atomic::Ordering, Arc}, task::{Context, Poll}, @@ -108,6 +110,9 @@ pub enum HandlerIn { /// Response to a [`HandlerOut::FindEnrForHolePunch`]. Returns the ENR if it was found. HolePunchEnr(Option, RelayMsg), + + /// Observed socket has been update. + SocketUpdated(SocketAddr), } /// Messages sent between a node on the network and `Handler`. @@ -266,11 +271,8 @@ impl Handler

{ // Attempt to bind to the socket before spinning up the send/recv tasks. let socket = Socket::new::

(socket_config).await?; - let nat_hole_puncher = NatHolePuncher { - new_peer_latest_relay: Default::default(), - ip_mode: config.ip_mode, - hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), - }; + let nat_hole_puncher = + NatHolePuncher::new(listen_socket.port(), &enr.read(), config.ip_mode); config .executor @@ -329,6 +331,12 @@ impl Handler

{ warn!("Failed to realy. Error: {}", e); } } + HandlerIn::SocketUpdated(socket) => { + let listen_port = self.listen_socket.port(); + let ip = socket.ip(); + let port = socket.port(); + self.nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip), Some(port)); + } } } Some(inbound_packet) = self.socket.recv.recv() => { @@ -342,8 +350,8 @@ impl Handler

{ // challenge. We process them here self.send_next_request(node_address).await; } - Some(Ok(node_address)) = self.nat_hole_puncher.hole_punch_tracker.next() => { - if let Err(e) = self.on_hole_punch_expired(node_address).await { + Some(Ok(peer_socket)) = self.nat_hole_puncher.next() => { + if let Err(e) = self.on_hole_punch_expired(peer_socket).await { warn!("Failed to keep hole punched for peer, error: {}", e); } } @@ -1312,18 +1320,23 @@ impl Handler

{ } } - /// Sends a packet to the send handler to be encoded and sent. - async fn send(&mut self, node_address: NodeAddress, packet: Outbound) { + /// Assembles and sends a [`Packet`]. + async fn send(&mut self, node_address: NodeAddress, packet: Packet) { + let socket_addr = node_address.socket_addr; let outbound_packet = socket::OutboundPacket { - node_address: node_address.clone(), + node_address, packet, }; - if let Err(e) = self.socket.send.send(outbound_packet).await { + self.send_outbound(socket_addr, outbound_packet.into()) + .await; + } + + /// Sends a packet to the send handler to be encoded and sent. + async fn send_outbound(&mut self, dst: SocketAddr, packet: Outbound) { + if let Err(e) = self.socket.send.send(packet).await { warn!("Failed to send outbound packet {}", e) } - self.nat_hole_puncher - .hole_punch_tracker - .insert(node_address); + self.nat_hole_puncher.track(dst); } /// Check if any banned nodes have served their time and unban them. @@ -1498,9 +1511,11 @@ impl NatHolePunch for Handler

{ } async fn on_hole_punch_expired( &mut self, - dst: Self::TNodeAddress, + dst: SocketAddr, ) -> Result<(), HolePunchError> { - Ok(self.send(dst, Outbound::KeepHolePunched).await) + Ok(self + .send_outbound(dst, Outbound::KeepHolePunched(dst)) + .await) } } @@ -1508,11 +1523,98 @@ impl NatHolePunch for Handler

{ struct NatHolePuncher { /// Ip mode as set in config. ip_mode: IpMode, + /// This node has been observed to be behind a NAT. + is_behind_nat: Option, /// The last peer to send us a new peer in a NODES response is stored as the new peer's /// potential relay until the first request to the new peer after its discovery is either /// responded or failed. new_peer_latest_relay: HashMap, /// Keeps track if this node needs to send a packet to a peer in order to keep a hole punched /// for it in its NAT. - hole_punch_tracker: HashSetDelay, + hole_punch_tracker: HashSetDelay, +} + +impl NatHolePuncher { + fn new(listen_port: u16, local_enr: &Enr, ip_mode: IpMode) -> Self { + let mut nat_hole_puncher = NatHolePuncher { + ip_mode, + is_behind_nat: None, + new_peer_latest_relay: Default::default(), + hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), + }; + // Optimistically only test one advertised socket, ipv4 has precedence. + match ( + local_enr.ip4(), + local_enr.udp4(), + local_enr.ip6(), + local_enr.udp6(), + ) { + (Some(ip), port, _, _) => { + nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip.into()), port); + } + (_, _, Some(ip6), port) => { + nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip6.into()), port); + } + (None, Some(port), _, _) => { + nat_hole_puncher.set_is_behind_nat(listen_port, None, Some(port)); + } + (_, _, None, Some(port)) => { + nat_hole_puncher.set_is_behind_nat(listen_port, None, Some(port)); + } + (None, None, None, None) => {} + } + nat_hole_puncher + } + + fn track(&mut self, peer_socket: SocketAddr) { + if self.is_behind_nat == Some(false) { + return; + } + self.hole_punch_tracker.insert(peer_socket); + } + + // Called when a new observed address is reported at start up or after a + // `Discv5Event::SocketUpdated(socket)` + fn set_is_behind_nat( + &mut self, + listen_port: u16, + observed_ip: Option, + observed_port: Option, + ) { + if Some(listen_port) != observed_port { + self.is_behind_nat = Some(true); + return; + } + // If the node cannot bind to the observed address at any of some random ports, we + // conclude it is behind a NAT. + let Some(ip) = observed_ip else { + return; + }; + let mut rng = rand::thread_rng(); + for _ in 0..4 { + let rnd_port: u16 = rng.gen_range(1024..=49151); + let Ok(addr) = format!("{}:{}", ip, rnd_port).parse() else { + return; + }; + if UdpSocket::bind(addr).is_ok() { + self.is_behind_nat = Some(false); + return; + } + } + debug!("Could not bind to any port tried on the observed address {}, setting nodes as behind NAT", ip); + self.is_behind_nat = Some(true); + } +} + +impl Stream for NatHolePuncher { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // Until ip voting is done and an observed public address is finalised, all nodes act as + // if they are behind a NAT. + if self.is_behind_nat == Some(false) || self.hole_punch_tracker.is_empty() { + return Poll::Pending; + } + self.hole_punch_tracker.poll_next_unpin(cx) + } } diff --git a/src/service.rs b/src/service.rs index dfcd41258..902481335 100644 --- a/src/service.rs +++ b/src/service.rs @@ -832,6 +832,12 @@ impl Service { updated = true; info!("Local UDP ip6 socket updated to: {}", new_ip6); self.send_event(Discv5Event::SocketUpdated(new_ip6)); + if let Err(e) = self + .handler_send + .send(HandlerIn::SocketUpdated(new_ip6)) + { + warn!("Failed to send socket update {}", e); + }; } Err(e) => { warn!("Failed to update local UDP ip6 socket. ip6: {}, error: {:?}", new_ip6, e); @@ -849,6 +855,13 @@ impl Service { updated = true; info!("Local UDP socket updated to: {}", new_ip4); self.send_event(Discv5Event::SocketUpdated(new_ip4)); + // Check if we are behind a NAT + if let Err(e) = self + .handler_send + .send(HandlerIn::SocketUpdated(new_ip4)) + { + warn!("Failed to send socket update {}", e); + }; } Err(e) => { warn!("Failed to update local UDP socket. ip: {}, error: {:?}", new_ip4, e); diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 79ba527c5..2722ba8c2 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -43,7 +43,7 @@ pub struct SocketConfig { /// Creates the UDP socket and handles the exit futures for the send/recv UDP handlers. pub struct Socket { - pub send: mpsc::Sender, + pub send: mpsc::Sender, pub recv: mpsc::Receiver, sender_exit: Option>, recv_exit: Option>, diff --git a/src/socket/send.rs b/src/socket/send.rs index 14bff241a..498174327 100644 --- a/src/socket/send.rs +++ b/src/socket/send.rs @@ -1,7 +1,7 @@ //! This is a standalone task that encodes and sends Discv5 UDP packets use crate::{metrics::METRICS, node_info::NodeAddress, packet::*, Executor}; use nat_hole_punch::impl_from_variant_wrap; -use std::sync::Arc; +use std::{net::SocketAddr, sync::Arc}; use tokio::{ net::UdpSocket, sync::{mpsc, oneshot}, @@ -9,17 +9,17 @@ use tokio::{ use tracing::{debug, trace, warn}; pub enum Outbound { - Packet(Packet), - KeepHolePunched, + Packet(OutboundPacket), + KeepHolePunched(SocketAddr), } -impl_from_variant_wrap!(, Packet, Outbound, Self::Packet); +impl_from_variant_wrap!(, OutboundPacket, Outbound, Self::Packet); pub struct OutboundPacket { /// The destination node address pub node_address: NodeAddress, /// The packet to be encoded. - pub packet: Outbound, + pub packet: Packet, } /// The main task that handles outbound UDP packets. @@ -27,7 +27,7 @@ pub(crate) struct SendHandler { /// The UDP send socket. send: Arc, /// The channel to respond to send requests. - handler_recv: mpsc::Receiver, + handler_recv: mpsc::Receiver, /// Exit channel to shutdown the handler. exit: oneshot::Receiver<()>, } @@ -39,7 +39,7 @@ impl SendHandler { pub(crate) fn spawn( executor: Box, send: Arc, - ) -> (mpsc::Sender, oneshot::Sender<()>) { + ) -> (mpsc::Sender, oneshot::Sender<()>) { let (exit_send, exit) = oneshot::channel(); let (handler_send, handler_recv) = mpsc::channel(30); @@ -62,22 +62,22 @@ impl SendHandler { loop { tokio::select! { Some(outbound) = self.handler_recv.recv() => { - let packet_bytes = match outbound.packet { - Outbound::Packet(packet) => { - let dst_id = outbound.node_address.node_id; - let encoded_packet = packet.encode::

(&dst_id); + let (dst_addr, encoded_pkt) = match outbound { + Outbound::Packet(outbound_packet) => { + let dst_id = outbound_packet.node_address.node_id; + let encoded_packet = outbound_packet.packet.encode::

(&dst_id); if encoded_packet.len() > MAX_PACKET_SIZE { warn!("Sending packet larger than max size: {} max: {}", encoded_packet.len(), MAX_PACKET_SIZE); } - encoded_packet + let dst_addr = outbound_packet.node_address.socket_addr; + (dst_addr, encoded_packet) } - Outbound::KeepHolePunched => vec![], + Outbound::KeepHolePunched(dst) => (dst, vec![]), }; - let dst_addr = outbound.node_address.socket_addr; - if let Err(e) = self.send.send_to(&packet_bytes, &dst_addr).await { + if let Err(e) = self.send.send_to(&encoded_pkt, &dst_addr).await { trace!("Could not send packet. Error: {:?}", e); } else { - METRICS.add_sent_bytes(packet_bytes.len()); + METRICS.add_sent_bytes(encoded_pkt.len()); } } _ = &mut self.exit => { From 103c2301ad2bb10c2447556beaa929be92f9a98d Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 6 Apr 2023 19:11:18 +0200 Subject: [PATCH 028/154] fixup! Only give work of keeping hole punched to nodes that aren't WAN reachable --- src/handler/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 5182cbe83..a8cd70aff 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1519,6 +1519,8 @@ impl NatHolePunch for Handler

{ } } +const DEFAULT_PORT_BIND_TRIES: usize = 4; + /// Types necessary implement trait [`NatHolePunch`]. struct NatHolePuncher { /// Ip mode as set in config. @@ -1591,7 +1593,7 @@ impl NatHolePuncher { return; }; let mut rng = rand::thread_rng(); - for _ in 0..4 { + for _ in 0..DEFAULT_PORT_BIND_TRIES { let rnd_port: u16 = rng.gen_range(1024..=49151); let Ok(addr) = format!("{}:{}", ip, rnd_port).parse() else { return; From 116d3505f5af1dbd83536d1a7e1f86fb5e6e4188 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 18 Apr 2023 14:11:00 +0200 Subject: [PATCH 029/154] Correct comment --- src/service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service.rs b/src/service.rs index 902481335..11967825d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -569,7 +569,7 @@ impl Service { to_request_enr = Some(enr); } } - // don't know of the ENR, request the update + // don't know of the ENR, drop the request _ => {} } if let Some(enr) = to_request_enr { From 79da62657a1b96895578f51baae2f5061411b45d Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 18 Apr 2023 14:57:52 +0200 Subject: [PATCH 030/154] Include nodes that discover reachable address earlier in small networks --- src/handler/mod.rs | 15 ++++++++++++--- src/lru_time_cache.rs | 4 ++++ src/service.rs | 17 +++++++++++------ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index a8cd70aff..1f9e2095d 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -111,8 +111,8 @@ pub enum HandlerIn { /// Response to a [`HandlerOut::FindEnrForHolePunch`]. Returns the ENR if it was found. HolePunchEnr(Option, RelayMsg), - /// Observed socket has been update. - SocketUpdated(SocketAddr), + /// Observed socket has been update. The old socket and the current socket. + SocketUpdate(Option, SocketAddr), } /// Messages sent between a node on the network and `Handler`. @@ -331,10 +331,19 @@ impl Handler

{ warn!("Failed to realy. Error: {}", e); } } - HandlerIn::SocketUpdated(socket) => { + HandlerIn::SocketUpdate(old_socket, socket) => { let listen_port = self.listen_socket.port(); let ip = socket.ip(); let port = socket.port(); + if old_socket.is_none() { + // This node goes from being unreachable to being reachable. Remove + // it's sessions to trigger a WHOAREYOU from peers. If the peer is + // running this implementation of discovery, this makes it possible + // for the local node to be inserted into its peers' kbuckets + // before the session they already had expires. Session duration, + // in this impl defaults to 24 hours. + self.sessions.clear() + } self.nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip), Some(port)); } } diff --git a/src/lru_time_cache.rs b/src/lru_time_cache.rs index fae149e3a..5be18e688 100644 --- a/src/lru_time_cache.rs +++ b/src/lru_time_cache.rs @@ -101,6 +101,10 @@ impl LruTimeCache { self.map.remove(&k); } } + + pub fn clear(&mut self) { + self.map.clear() + } } #[cfg(test)] diff --git a/src/service.rs b/src/service.rs index 11967825d..aec54c95d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -832,9 +832,12 @@ impl Service { updated = true; info!("Local UDP ip6 socket updated to: {}", new_ip6); self.send_event(Discv5Event::SocketUpdated(new_ip6)); - if let Err(e) = self - .handler_send - .send(HandlerIn::SocketUpdated(new_ip6)) + // Check if we are behind a NAT + if let Err(e) = + self.handler_send.send(HandlerIn::SocketUpdate( + local_ip6_socket.map(SocketAddr::V6), + new_ip6, + )) { warn!("Failed to send socket update {}", e); }; @@ -856,9 +859,11 @@ impl Service { info!("Local UDP socket updated to: {}", new_ip4); self.send_event(Discv5Event::SocketUpdated(new_ip4)); // Check if we are behind a NAT - if let Err(e) = self - .handler_send - .send(HandlerIn::SocketUpdated(new_ip4)) + if let Err(e) = + self.handler_send.send(HandlerIn::SocketUpdate( + local_ip4_socket.map(SocketAddr::V4), + new_ip4, + )) { warn!("Failed to send socket update {}", e); }; From 0eb62dc5cf76b13ba3d29d308b94f038912348a6 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 18 Apr 2023 14:59:08 +0200 Subject: [PATCH 031/154] fixup! Include nodes that discover reachable address earlier in small networks --- src/handler/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 1f9e2095d..f02a11949 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -337,7 +337,7 @@ impl Handler

{ let port = socket.port(); if old_socket.is_none() { // This node goes from being unreachable to being reachable. Remove - // it's sessions to trigger a WHOAREYOU from peers. If the peer is + // its sessions to trigger a WHOAREYOU from peers. If the peer is // running this implementation of discovery, this makes it possible // for the local node to be inserted into its peers' kbuckets // before the session they already had expires. Session duration, From 02c9a8e768d2a43faacd15f8fe58b26c38d05ca6 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 18 Apr 2023 16:19:01 +0200 Subject: [PATCH 032/154] Limit sessions with unreachable enrs --- src/error.rs | 2 + src/handler/mod.rs | 120 ++++++++++++---------- src/handler/{ => sessions}/crypto/ecdh.rs | 0 src/handler/{ => sessions}/crypto/mod.rs | 0 src/handler/sessions/limiter.rs | 47 +++++++++ src/handler/sessions/mod.rs | 26 +++++ src/handler/{ => sessions}/session.rs | 94 ++++++++++------- src/lru_time_cache.rs | 53 +++++++--- 8 files changed, 235 insertions(+), 107 deletions(-) rename src/handler/{ => sessions}/crypto/ecdh.rs (100%) rename src/handler/{ => sessions}/crypto/mod.rs (100%) create mode 100644 src/handler/sessions/limiter.rs create mode 100644 src/handler/sessions/mod.rs rename src/handler/{ => sessions}/session.rs (84%) diff --git a/src/error.rs b/src/error.rs index 49fd92ab1..eac649179 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,6 +7,8 @@ use std::fmt; pub enum Discv5Error { /// An invalid ENR was received. InvalidEnr, + /// An invalid ENR was received. + LimitSessionsUnreachableEnr, /// The public key type is known. UnknownPublicKey, /// The ENR key used is not supported. diff --git a/src/handler/mod.rs b/src/handler/mod.rs index f02a11949..84fcd9b65 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -59,19 +59,17 @@ use tokio::sync::{mpsc, oneshot}; use tracing::{debug, error, trace, warn}; mod active_requests; -mod crypto; mod request_call; -mod session; +mod sessions; mod tests; pub use crate::node_info::{NodeAddress, NodeContact}; use crate::metrics::METRICS; -use crate::lru_time_cache::LruTimeCache; use active_requests::ActiveRequests; use request_call::RequestCall; -use session::Session; +use sessions::{Session, Sessions}; // The time interval to check banned peer timeouts and unban peers when the timeout has elapsed (in // seconds). @@ -205,7 +203,7 @@ pub struct Handler { /// Currently in-progress outbound handshakes (WHOAREYOU packets) with peers. active_challenges: HashMapDelay, /// Established sessions with peers. - sessions: LruTimeCache, + sessions: Sessions, /// The channel to receive messages from the application layer. service_recv: mpsc::UnboundedReceiver, /// The channel to send messages to the application layer. @@ -274,6 +272,8 @@ impl Handler

{ let nat_hole_puncher = NatHolePuncher::new(listen_socket.port(), &enr.read(), config.ip_mode); + let sessions = Sessions::new(config.session_cache_capacity, config.session_timeout); + config .executor .clone() @@ -287,10 +287,7 @@ impl Handler

{ active_requests: ActiveRequests::new(config.request_timeout), pending_requests: HashMap::new(), filter_expected_responses, - sessions: LruTimeCache::new( - config.session_timeout, - Some(config.session_cache_capacity), - ), + sessions, active_challenges: HashMapDelay::new(config.request_timeout), service_recv, service_send, @@ -342,7 +339,7 @@ impl Handler

{ // for the local node to be inserted into its peers' kbuckets // before the session they already had expires. Session duration, // in this impl defaults to 24 hours. - self.sessions.clear() + self.sessions.cache.clear() } self.nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip), Some(port)); } @@ -545,7 +542,7 @@ impl Handler

{ } let (packet, initiating_session) = { - if let Some(session) = self.sessions.get_mut(&node_address) { + if let Some(session) = self.sessions.cache.get_mut(&node_address) { // Encrypt the message and send let request = match &request_id { HandlerReqId::Internal(id) | HandlerReqId::External(id) => Request { @@ -588,7 +585,7 @@ impl Handler

{ /// Sends an RPC Response. async fn send_response(&mut self, node_address: NodeAddress, response: Response) { // Check for an established session - if let Some(session) = self.sessions.get_mut(&node_address) { + if let Some(session) = self.sessions.cache.get_mut(&node_address) { // Encrypt the message and send let packet = match session.encrypt_notification::

(self.node_id, &response.encode()) { Ok(packet) => packet, @@ -769,14 +766,8 @@ impl Handler

{ self.send(node_address.clone(), auth_packet.into()).await; // Notify the application that the session has been established - self.service_send - .send(HandlerOut::Established( - enr, - node_address.socket_addr, - connection_direction, - )) - .await - .unwrap_or_else(|e| warn!("Error with sending channel: {}", e)); + self.new_connection(enr, node_address.socket_addr, connection_direction) + .await; } None => { // Don't know the ENR. Establish the session, but request an ENR also @@ -841,14 +832,18 @@ impl Handler

{ ); if let Some(challenge) = self.active_challenges.remove(&node_address) { + let local_key = self.key.clone(); + let local_id = self.node_id; match Session::establish_from_challenge( - self.key.clone(), - &self.node_id, + local_key, + &local_id, &node_address.node_id, challenge, id_nonce_sig, ephem_pubkey, enr_record, + &node_address, + &mut self.sessions, ) { Ok((session, enr)) => { // Receiving an AuthResponse must give us an up-to-date view of the node ENR. @@ -858,17 +853,12 @@ impl Handler

{ // Notify the application // The session established here are from WHOAREYOU packets that we sent. // This occurs when a node established a connection with us. - if let Err(e) = self - .service_send - .send(HandlerOut::Established( - enr, - node_address.socket_addr, - ConnectionDirection::Incoming, - )) - .await - { - warn!("Failed to inform of established session {}", e) - } + self.new_connection( + enr, + node_address.socket_addr, + ConnectionDirection::Incoming, + ) + .await; self.new_session(node_address.clone(), session); self.nat_hole_puncher .new_peer_latest_relay @@ -903,6 +893,9 @@ impl Handler

{ // insert back the challenge self.active_challenges.insert(node_address, challenge); } + Err(Discv5Error::LimitSessionsUnreachableEnr) => { + warn!("Limit reached for sessions with unreachable ENRs. Dropping session."); + } Err(e) => { warn!( "Invalid Authentication header. Dropping session. Error: {:?}", @@ -971,7 +964,7 @@ impl Handler

{ authenticated_data: &[u8], ) { // check if we have an available session - let Some(session) = self.sessions.get_mut(&node_address) else { + let Some(session) = self.sessions.cache.get_mut(&node_address) else { warn!( "Dropping message. Error: {}, {}", Discv5Error::SessionNotEstablished, @@ -1030,17 +1023,12 @@ impl Handler

{ // This can occur when we try to dial a node without an // ENR. In this case we have attempted to establish the // connection, so this is an outgoing connection. - if let Err(e) = self - .service_send - .send(HandlerOut::Established( - enr, - node_address.socket_addr, - ConnectionDirection::Outgoing, - )) - .await - { - warn!("Failed to inform established outgoing connection {}", e) - } + self.new_connection( + enr, + node_address.socket_addr, + ConnectionDirection::Outgoing, + ) + .await; return; } } @@ -1070,7 +1058,7 @@ impl Handler

{ authenticated_data: &[u8], ) { // check if we have an available session - let Some(session) = self.sessions.get_mut(&node_address) else { + let Some(session) = self.sessions.cache.get_mut(&node_address) else { // no session exists trace!("Received a message without a session. {}", node_address); trace!("Requesting a WHOAREYOU packet to be sent."); @@ -1181,7 +1169,7 @@ impl Handler

{ socket_addr, node_id, }; - if self.sessions.peek(&new_peer_node_address).is_none() { + if self.sessions.cache.peek(&new_peer_node_address).is_none() { self.nat_hole_puncher .new_peer_latest_relay .insert(node_id, node_address.clone()); @@ -1255,13 +1243,13 @@ impl Handler

{ } fn new_session(&mut self, node_address: NodeAddress, session: Session) { - if let Some(current_session) = self.sessions.get_mut(&node_address) { + if let Some(current_session) = self.sessions.cache.get_mut(&node_address) { current_session.update(session); } else { - self.sessions.insert(node_address, session); + self.sessions.cache.insert(node_address, session); METRICS .active_sessions - .store(self.sessions.len(), Ordering::Relaxed); + .store(self.sessions.cache.len(), Ordering::Relaxed); } } @@ -1304,10 +1292,10 @@ impl Handler

{ remove_session: bool, ) { if remove_session { - self.sessions.remove(node_address); + self.sessions.cache.remove(node_address); METRICS .active_sessions - .store(self.sessions.len(), Ordering::Relaxed); + .store(self.sessions.cache.len(), Ordering::Relaxed); } if let Some(to_remove) = self.pending_requests.remove(node_address) { for PendingRequest { request_id, .. } in to_remove { @@ -1373,7 +1361,7 @@ impl Handler

{ Ok(contact) => contact.node_address(), Err(e) => return Err(HolePunchError::RelayError(e.into())), }; - if let Some(session) = self.sessions.get_mut(&tgt_node_address) { + if let Some(session) = self.sessions.cache.get_mut(&tgt_node_address) { trace!( "Sending notif to target {}. relay msg: {}", tgt_node_address.node_id, @@ -1401,6 +1389,24 @@ impl Handler

{ )) } } + + async fn new_connection( + &mut self, + enr: Enr, + socket_addr: SocketAddr, + conn_dir: ConnectionDirection, + ) { + if let Err(e) = self + .service_send + .send(HandlerOut::Established(enr, socket_addr, conn_dir)) + .await + { + warn!( + "Failed to inform of established connection {}, {}", + conn_dir, e + ) + } + } } #[async_trait] @@ -1416,10 +1422,10 @@ impl NatHolePunch for Handler

{ target_session_index: Self::TNodeAddress, ) -> Result<(), HolePunchError> { // Another hole punch process with this target may have just completed. - if self.sessions.get(&target_session_index).is_some() { + if self.sessions.cache.get(&target_session_index).is_some() { return Ok(()); } - if let Some(session) = self.sessions.get_mut(&relay) { + if let Some(session) = self.sessions.cache.get_mut(&relay) { let relay_init_notif = RelayInit( local_enr, target_session_index.node_id.raw(), @@ -1487,7 +1493,7 @@ impl NatHolePunch for Handler

{ }; // A session may already have been established. - if self.sessions.get(&initiator_node_address).is_some() { + if self.sessions.cache.get(&initiator_node_address).is_some() { trace!( "Session already established with initiator: {}", initiator_node_address @@ -1531,7 +1537,7 @@ impl NatHolePunch for Handler

{ const DEFAULT_PORT_BIND_TRIES: usize = 4; /// Types necessary implement trait [`NatHolePunch`]. -struct NatHolePuncher { +pub struct NatHolePuncher { /// Ip mode as set in config. ip_mode: IpMode, /// This node has been observed to be behind a NAT. diff --git a/src/handler/crypto/ecdh.rs b/src/handler/sessions/crypto/ecdh.rs similarity index 100% rename from src/handler/crypto/ecdh.rs rename to src/handler/sessions/crypto/ecdh.rs diff --git a/src/handler/crypto/mod.rs b/src/handler/sessions/crypto/mod.rs similarity index 100% rename from src/handler/crypto/mod.rs rename to src/handler/sessions/crypto/mod.rs diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs new file mode 100644 index 000000000..c0bd7df00 --- /dev/null +++ b/src/handler/sessions/limiter.rs @@ -0,0 +1,47 @@ +use crate::{node_info::NodeAddress, Discv5Error, Enr}; +use std::collections::HashSet; + +/// The default number of peers to accept sessions with, i.e. cater requests for, at a time. +/// Benevolent peers of this type could for example be symmetrically NAT:ed nodes or nodes that +/// have recently joined the network and are still unaware of their externally reachable socket, +/// relying on their peers to discover it. +const DEFAULT_MAX_SESSIONS_UNREACHABLE_ENR: usize = 10; + +pub(crate) struct SessionLimiter { + /// Keeps track of the sessions held for peers with unreachable ENRs. These could be peers yet + /// to discover their externally reachable socket or symmetrically NAT:ed peers that, + /// naturally, will never discover one externally reachable socket. + sessions_unreachable_enr_tracker: HashSet, + /// Receiver of expired sessions. + rx_expired_sessions: futures::channel::mpsc::Receiver, +} + +impl SessionLimiter { + pub fn new(rx_expired_sessions: futures::channel::mpsc::Receiver) -> Self { + SessionLimiter { + sessions_unreachable_enr_tracker: Default::default(), + rx_expired_sessions, + } + } + + // Checks if a session with this peer should be allowed at this given time. + pub fn track_sessions_unreachable_enr( + &mut self, + node_address: &NodeAddress, + enr: &Enr, + ) -> Result<(), Discv5Error> { + // Empty buffer of expired sessions, and remove any which belong to unreachable ENRs. + while let Ok(Some(session_index)) = self.rx_expired_sessions.try_next() { + self.sessions_unreachable_enr_tracker.remove(&session_index); + } + if enr.udp4_socket().is_none() && enr.udp6_socket().is_none() { + // Peer is unreachable + if self.sessions_unreachable_enr_tracker.len() >= DEFAULT_MAX_SESSIONS_UNREACHABLE_ENR { + return Err(Discv5Error::LimitSessionsUnreachableEnr); + } + self.sessions_unreachable_enr_tracker + .insert(node_address.clone()); + } + Ok(()) + } +} diff --git a/src/handler/sessions/mod.rs b/src/handler/sessions/mod.rs new file mode 100644 index 000000000..7f544e0c7 --- /dev/null +++ b/src/handler/sessions/mod.rs @@ -0,0 +1,26 @@ +use crate::{lru_time_cache::LruTimeCache, node_info::NodeAddress}; +use std::time::Duration; + +mod crypto; +mod limiter; +mod session; + +use limiter::SessionLimiter; +pub use session::Session; + +pub struct Sessions { + pub cache: LruTimeCache, + limiter: SessionLimiter, +} + +impl Sessions { + pub fn new(cache_capacity: usize, entry_ttl: Duration) -> Self { + let (tx, rx) = futures::channel::mpsc::channel::(cache_capacity); + let sessions = LruTimeCache::new(entry_ttl, Some(cache_capacity), Some(tx)); + let limiter = SessionLimiter::new(rx); + Sessions { + cache: sessions, + limiter, + } + } +} diff --git a/src/handler/session.rs b/src/handler/sessions/session.rs similarity index 84% rename from src/handler/session.rs rename to src/handler/sessions/session.rs index f175be663..26b56126b 100644 --- a/src/handler/session.rs +++ b/src/handler/sessions/session.rs @@ -1,15 +1,23 @@ use super::*; use crate::{ + handler::Challenge, node_info::NodeContact, packet::{ - ChallengeData, Packet, PacketHeader, PacketKind, ProtocolIdentity, MESSAGE_NONCE_LENGTH, + ChallengeData, MessageNonce, Packet, PacketHeader, PacketKind, ProtocolIdentity, + MESSAGE_NONCE_LENGTH, }, + rpc::RequestId, + Discv5Error, Enr, }; + use enr::{CombinedKey, NodeId}; +use parking_lot::RwLock; +use std::sync::Arc; +use tracing::warn; use zeroize::Zeroize; #[derive(Zeroize, PartialEq)] -pub(crate) struct Keys { +pub struct Keys { /// The encryption key. encryption_key: [u8; 16], /// The decryption key. @@ -18,7 +26,7 @@ pub(crate) struct Keys { /// A Session containing the encryption/decryption keys. These are kept individually for a given /// node. -pub(crate) struct Session { +pub struct Session { /// The current keys used to encrypt/decrypt messages. keys: Keys, /// If a new handshake is being established, the older keys are maintained as race @@ -182,32 +190,38 @@ impl Session { id_nonce_sig: &[u8], ephem_pubkey: &[u8], enr_record: Option, + node_address: &NodeAddress, + sessions: &mut Sessions, ) -> Result<(Session, Enr), Discv5Error> { // check and verify a potential ENR update - // Duplicate code here to avoid cloning an ENR - let remote_public_key = { - let enr = match (enr_record.as_ref(), challenge.remote_enr.as_ref()) { - (Some(new_enr), Some(known_enr)) => { - if new_enr.seq() > known_enr.seq() { - new_enr - } else { - known_enr - } - } - (Some(new_enr), None) => new_enr, - (None, Some(known_enr)) => known_enr, - (None, None) => { - warn!( - "Peer did not respond with their ENR. Session could not be established. Node: {}", - remote_id - ); - return Err(Discv5Error::SessionNotEstablished); + // Avoid cloning an ENR + let session_enr = match (enr_record, challenge.remote_enr.as_ref()) { + (Some(new_enr), Some(known_enr)) => { + if new_enr.seq() > known_enr.seq() { + MostRecentEnr::Handshake(new_enr) + } else { + MostRecentEnr::Challenge(known_enr) } - }; - enr.public_key() + } + (Some(new_enr), None) => MostRecentEnr::Handshake(new_enr), + (None, Some(known_enr)) => MostRecentEnr::Challenge(known_enr), + (None, None) => { + warn!( + "Peer did not respond with their ENR. Session could not be established. Node: {}", + remote_id + ); + return Err(Discv5Error::SessionNotEstablished); + } }; + // Avoid unnecessary key derivation computation, first verify session candidate against + // current sessions state + sessions + .limiter + .track_sessions_unreachable_enr(node_address, session_enr.enr())?; + let remote_public_key = session_enr.enr().public_key(); + // verify the auth header nonce if !crypto::verify_authentication_nonce( &remote_public_key, @@ -236,19 +250,15 @@ impl Session { decryption_key, }; - // Takes ownership of the provided ENRs - Slightly annoying code duplication, but avoids - // cloning ENRs - let session_enr = match (enr_record, challenge.remote_enr) { - (Some(new_enr), Some(known_enr)) => { - if new_enr.seq() > known_enr.seq() { - new_enr - } else { - known_enr - } + let session_enr = match session_enr { + MostRecentEnr::Challenge(_) => { + let Challenge { + data: _, + remote_enr, + } = challenge; + remote_enr.unwrap() } - (Some(new_enr), None) => new_enr, - (None, Some(known_enr)) => known_enr, - (None, None) => unreachable!("Checked in the first match above"), + MostRecentEnr::Handshake(enr) => enr, }; Ok((Session::new(keys), session_enr)) @@ -307,3 +317,17 @@ impl Session { Ok((packet, session)) } } + +enum MostRecentEnr<'a> { + Challenge(&'a Enr), + Handshake(Enr), +} + +impl<'a> MostRecentEnr<'a> { + fn enr(&self) -> &Enr { + match *self { + Self::Challenge(enr) => enr, + Self::Handshake(ref enr) => enr, + } + } +} diff --git a/src/lru_time_cache.rs b/src/lru_time_cache.rs index 5be18e688..1248f19d5 100644 --- a/src/lru_time_cache.rs +++ b/src/lru_time_cache.rs @@ -1,8 +1,10 @@ +use futures::channel::mpsc; use hashlink::LinkedHashMap; use std::{ hash::Hash, time::{Duration, Instant}, }; +use tracing::warn; pub struct LruTimeCache { map: LinkedHashMap, @@ -10,10 +12,16 @@ pub struct LruTimeCache { ttl: Duration, /// The max size of the cache. capacity: usize, + /// Channel to send expiry notifications on. + tx: Option>, } impl LruTimeCache { - pub fn new(ttl: Duration, capacity: Option) -> LruTimeCache { + pub fn new( + ttl: Duration, + capacity: Option, + tx: Option>, + ) -> LruTimeCache { let capacity = if let Some(cap) = capacity { cap } else { @@ -23,6 +31,7 @@ impl LruTimeCache { map: LinkedHashMap::new(), ttl, capacity, + tx, } } @@ -83,7 +92,13 @@ impl LruTimeCache { /// Removes a key-value pair from the cache, returning the value at the key if the key /// was previously in the map. pub fn remove(&mut self, key: &K) -> Option { - self.map.remove(key).map(|v| v.0) + let v = self.map.remove(key).map(|v| v.0); + if let Some(ref mut tx) = self.tx { + if let Err(e) = tx.try_send(key.clone()) { + warn!("remove failed, {}", e); + } + } + v } /// Removes expired items from the cache. @@ -97,8 +112,16 @@ impl LruTimeCache { expired_keys.push(key.clone()); } - for k in expired_keys { - self.map.remove(&k); + for k in expired_keys.iter() { + self.map.remove(k); + } + + if let Some(ref mut tx) = self.tx { + for k in expired_keys { + if let Err(e) = tx.try_send(k) { + warn!("remove expired failed, {}", e); + } + } } } @@ -114,7 +137,7 @@ mod tests { #[test] fn insert() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), None); + let mut cache = LruTimeCache::new(Duration::from_secs(10), None, None); cache.insert(1, 10); cache.insert(2, 20); @@ -127,7 +150,7 @@ mod tests { #[test] fn capacity() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2)); + let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2), None); cache.insert(1, 10); cache.insert(2, 20); @@ -141,7 +164,7 @@ mod tests { #[test] fn get() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2)); + let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2), None); cache.insert(1, 10); cache.insert(2, 20); @@ -156,7 +179,7 @@ mod tests { #[test] fn get_mut() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), None); + let mut cache = LruTimeCache::new(Duration::from_secs(10), None, None); cache.insert(1, 10); let v = cache.get_mut(&1).expect("should have value"); @@ -167,7 +190,7 @@ mod tests { #[test] fn peek() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2)); + let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2), None); cache.insert(1, 10); cache.insert(2, 20); @@ -181,7 +204,7 @@ mod tests { #[test] fn len() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), None); + let mut cache = LruTimeCache::new(Duration::from_secs(10), None, None); assert_eq!(0, cache.len()); @@ -193,7 +216,7 @@ mod tests { #[test] fn remove() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), None); + let mut cache = LruTimeCache::new(Duration::from_secs(10), None, None); cache.insert(1, 10); assert_eq!(Some(10), cache.remove(&1)); @@ -209,7 +232,7 @@ mod tests { #[test] fn get() { - let mut cache = LruTimeCache::new(TTL, None); + let mut cache = LruTimeCache::new(TTL, None, None); cache.insert(1, 10); assert_eq!(Some(&10), cache.get(&1)); @@ -219,7 +242,7 @@ mod tests { #[test] fn peek() { - let mut cache = LruTimeCache::new(TTL, None); + let mut cache = LruTimeCache::new(TTL, None, None); cache.insert(1, 10); assert_eq!(Some(&10), cache.peek(&1)); @@ -229,7 +252,7 @@ mod tests { #[test] fn len() { - let mut cache = LruTimeCache::new(TTL, None); + let mut cache = LruTimeCache::new(TTL, None, None); cache.insert(1, 10); assert_eq!(1, cache.len()); @@ -239,7 +262,7 @@ mod tests { #[test] fn ttl() { - let mut cache = LruTimeCache::new(TTL, None); + let mut cache = LruTimeCache::new(TTL, None, None); cache.insert(1, 10); sleep(TTL / 4); cache.insert(2, 20); From 093f59851f59ec131c84d227cee07fe90a537afd Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 20 Apr 2023 16:11:51 +0200 Subject: [PATCH 033/154] Set limit for sessions with peers with unreachable ENRs in config --- src/config.rs | 12 +++++++++++ src/handler/mod.rs | 9 +++++--- src/handler/sessions/limiter.rs | 37 ++++++++++++++++++++++++++------- src/handler/sessions/mod.rs | 8 +++++-- 4 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/config.rs b/src/config.rs index da3b68ad2..266be0ca9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -99,6 +99,11 @@ pub struct Discv5Config { /// A custom executor which can spawn the discv5 tasks. This must be a tokio runtime, with /// timing support. By default, the executor that created the discv5 struct will be used. pub executor: Option>, + + /// The limit for peers with unreachable ENRs. Benevolent examples of such peers are peers + /// that are discovering their externally reachable socket and peers behind symmetric NAT. + /// Default are 250. Minimum is 1. + pub unreachable_enr_limit: Option, } impl Default for Discv5Config { @@ -138,6 +143,7 @@ impl Default for Discv5Config { ban_duration: Some(Duration::from_secs(3600)), // 1 hour ip_mode: IpMode::default(), executor: None, + unreachable_enr_limit: None, } } } @@ -314,6 +320,12 @@ impl Discv5ConfigBuilder { self } + /// Whether to enable the incoming packet filter. + pub fn unreachable_enr_limit(&mut self, peer_limit: Option) -> &mut Self { + self.config.unreachable_enr_limit = peer_limit; + self + } + pub fn build(&mut self) -> Discv5Config { // If an executor is not provided, assume a current tokio runtime is running. if self.config.executor.is_none() { diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 84fcd9b65..c66b39304 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -63,9 +63,8 @@ mod request_call; mod sessions; mod tests; -pub use crate::node_info::{NodeAddress, NodeContact}; - use crate::metrics::METRICS; +pub use crate::node_info::{NodeAddress, NodeContact}; use active_requests::ActiveRequests; use request_call::RequestCall; @@ -272,7 +271,11 @@ impl Handler

{ let nat_hole_puncher = NatHolePuncher::new(listen_socket.port(), &enr.read(), config.ip_mode); - let sessions = Sessions::new(config.session_cache_capacity, config.session_timeout); + let sessions = Sessions::new( + config.session_cache_capacity, + config.session_timeout, + config.unreachable_enr_limit, + ); config .executor diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs index c0bd7df00..8220e18f0 100644 --- a/src/handler/sessions/limiter.rs +++ b/src/handler/sessions/limiter.rs @@ -1,11 +1,14 @@ use crate::{node_info::NodeAddress, Discv5Error, Enr}; use std::collections::HashSet; +use tracing::warn; -/// The default number of peers to accept sessions with, i.e. cater requests for, at a time. -/// Benevolent peers of this type could for example be symmetrically NAT:ed nodes or nodes that -/// have recently joined the network and are still unaware of their externally reachable socket, -/// relying on their peers to discover it. -const DEFAULT_MAX_SESSIONS_UNREACHABLE_ENR: usize = 10; +/// The minimum number of peers to accept sessions with that have an unreachable ENR, i.e. cater +/// requests for, at a time. Benevolent peers of this type could for example be symmetrically +/// NAT:ed nodes or nodes that have recently joined the network and are still unaware of their +/// externally reachable socket, relying on their peers to discover it. +pub const MIN_SESSIONS_UNREACHABLE_ENR: usize = 1; +/// The default number of peers to accept sessions with that have an unreachable ENR. +pub const DEFAULT_SESSIONS_UNREACHABLE_ENR: usize = 250; pub(crate) struct SessionLimiter { /// Keeps track of the sessions held for peers with unreachable ENRs. These could be peers yet @@ -14,13 +17,33 @@ pub(crate) struct SessionLimiter { sessions_unreachable_enr_tracker: HashSet, /// Receiver of expired sessions. rx_expired_sessions: futures::channel::mpsc::Receiver, + /// The max number of sessions to peers with unreachable ENRs at a time. + limit: usize, } impl SessionLimiter { - pub fn new(rx_expired_sessions: futures::channel::mpsc::Receiver) -> Self { + pub fn new( + rx_expired_sessions: futures::channel::mpsc::Receiver, + unreachable_enr_limit: Option, + ) -> Self { + let limit = match unreachable_enr_limit { + Some(peer_count) => { + if peer_count < MIN_SESSIONS_UNREACHABLE_ENR { + warn!( + "unreachable ENR limit from config too low, setting to minimum {}", + MIN_SESSIONS_UNREACHABLE_ENR + ); + MIN_SESSIONS_UNREACHABLE_ENR + } else { + peer_count + } + } + None => DEFAULT_SESSIONS_UNREACHABLE_ENR, + }; SessionLimiter { sessions_unreachable_enr_tracker: Default::default(), rx_expired_sessions, + limit, } } @@ -36,7 +59,7 @@ impl SessionLimiter { } if enr.udp4_socket().is_none() && enr.udp6_socket().is_none() { // Peer is unreachable - if self.sessions_unreachable_enr_tracker.len() >= DEFAULT_MAX_SESSIONS_UNREACHABLE_ENR { + if self.sessions_unreachable_enr_tracker.len() >= self.limit { return Err(Discv5Error::LimitSessionsUnreachableEnr); } self.sessions_unreachable_enr_tracker diff --git a/src/handler/sessions/mod.rs b/src/handler/sessions/mod.rs index 7f544e0c7..459d93505 100644 --- a/src/handler/sessions/mod.rs +++ b/src/handler/sessions/mod.rs @@ -14,10 +14,14 @@ pub struct Sessions { } impl Sessions { - pub fn new(cache_capacity: usize, entry_ttl: Duration) -> Self { + pub fn new( + cache_capacity: usize, + entry_ttl: Duration, + unreachable_enr_limit: Option, + ) -> Self { let (tx, rx) = futures::channel::mpsc::channel::(cache_capacity); let sessions = LruTimeCache::new(entry_ttl, Some(cache_capacity), Some(tx)); - let limiter = SessionLimiter::new(rx); + let limiter = SessionLimiter::new(rx, unreachable_enr_limit); Sessions { cache: sessions, limiter, From bb0e597851b6df98833ec787857b152005808c43 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 21 Apr 2023 11:16:19 +0200 Subject: [PATCH 034/154] Allow unlimited sessions with unreachable ENRs like in discv5.0 --- src/config.rs | 4 ++-- src/handler/sessions/limiter.rs | 25 +++++++++---------------- src/handler/sessions/mod.rs | 4 ++-- src/handler/sessions/session.rs | 7 ++++--- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/config.rs b/src/config.rs index 266be0ca9..19b7d2204 100644 --- a/src/config.rs +++ b/src/config.rs @@ -100,9 +100,9 @@ pub struct Discv5Config { /// timing support. By default, the executor that created the discv5 struct will be used. pub executor: Option>, - /// The limit for peers with unreachable ENRs. Benevolent examples of such peers are peers + /// The max limit for peers with unreachable ENRs. Benevolent examples of such peers are peers /// that are discovering their externally reachable socket and peers behind symmetric NAT. - /// Default are 250. Minimum is 1. + /// Default is no limit. Minimum is 1. pub unreachable_enr_limit: Option, } diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs index 8220e18f0..9f7a668a5 100644 --- a/src/handler/sessions/limiter.rs +++ b/src/handler/sessions/limiter.rs @@ -7,8 +7,6 @@ use tracing::warn; /// NAT:ed nodes or nodes that have recently joined the network and are still unaware of their /// externally reachable socket, relying on their peers to discover it. pub const MIN_SESSIONS_UNREACHABLE_ENR: usize = 1; -/// The default number of peers to accept sessions with that have an unreachable ENR. -pub const DEFAULT_SESSIONS_UNREACHABLE_ENR: usize = 250; pub(crate) struct SessionLimiter { /// Keeps track of the sessions held for peers with unreachable ENRs. These could be peers yet @@ -24,21 +22,16 @@ pub(crate) struct SessionLimiter { impl SessionLimiter { pub fn new( rx_expired_sessions: futures::channel::mpsc::Receiver, - unreachable_enr_limit: Option, + unreachable_enr_limit: usize, ) -> Self { - let limit = match unreachable_enr_limit { - Some(peer_count) => { - if peer_count < MIN_SESSIONS_UNREACHABLE_ENR { - warn!( - "unreachable ENR limit from config too low, setting to minimum {}", - MIN_SESSIONS_UNREACHABLE_ENR - ); - MIN_SESSIONS_UNREACHABLE_ENR - } else { - peer_count - } - } - None => DEFAULT_SESSIONS_UNREACHABLE_ENR, + let limit = if unreachable_enr_limit < MIN_SESSIONS_UNREACHABLE_ENR { + warn!( + "unreachable ENR limit from config too low, setting to minimum {}", + MIN_SESSIONS_UNREACHABLE_ENR + ); + MIN_SESSIONS_UNREACHABLE_ENR + } else { + unreachable_enr_limit }; SessionLimiter { sessions_unreachable_enr_tracker: Default::default(), diff --git a/src/handler/sessions/mod.rs b/src/handler/sessions/mod.rs index 459d93505..097140552 100644 --- a/src/handler/sessions/mod.rs +++ b/src/handler/sessions/mod.rs @@ -10,7 +10,7 @@ pub use session::Session; pub struct Sessions { pub cache: LruTimeCache, - limiter: SessionLimiter, + limiter: Option, } impl Sessions { @@ -21,7 +21,7 @@ impl Sessions { ) -> Self { let (tx, rx) = futures::channel::mpsc::channel::(cache_capacity); let sessions = LruTimeCache::new(entry_ttl, Some(cache_capacity), Some(tx)); - let limiter = SessionLimiter::new(rx, unreachable_enr_limit); + let limiter = unreachable_enr_limit.map(|limit| SessionLimiter::new(rx, limit)); Sessions { cache: sessions, limiter, diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index 26b56126b..8071b9aa7 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -217,9 +217,10 @@ impl Session { // Avoid unnecessary key derivation computation, first verify session candidate against // current sessions state - sessions - .limiter - .track_sessions_unreachable_enr(node_address, session_enr.enr())?; + if let Some(ref mut limiter) = sessions.limiter { + limiter.track_sessions_unreachable_enr(node_address, session_enr.enr())?; + } + let remote_public_key = session_enr.enr().public_key(); // verify the auth header nonce From 32a7eef52d45c9f7a1c9fd45ff3781ce9272fd2d Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 21 Apr 2023 23:36:37 +0200 Subject: [PATCH 035/154] Clean up fs --- src/handler/mod.rs | 112 ++------------------------------ src/handler/nat_hole_puncher.rs | 100 ++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 107 deletions(-) create mode 100644 src/handler/nat_hole_puncher.rs diff --git a/src/handler/mod.rs b/src/handler/mod.rs index c66b39304..82e69fe29 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -34,22 +34,20 @@ use crate::{ rpc::{Message, Request, RequestBody, RequestId, Response, ResponseBody}, socket, socket::{FilterConfig, Outbound, Socket}, - Enr, IpMode, + Enr, }; use async_trait::async_trait; -use delay_map::{HashMapDelay, HashSetDelay}; +use delay_map::HashMapDelay; use enr::{CombinedKey, NodeId}; use futures::prelude::*; -use mio::net::UdpSocket; use nat_hole_punch::*; use parking_lot::RwLock; -use rand::prelude::*; use std::{ collections::HashMap, convert::TryFrom, default::Default, marker::PhantomData, - net::{IpAddr, SocketAddr}, + net::SocketAddr, pin::Pin, sync::{atomic::Ordering, Arc}, task::{Context, Poll}, @@ -59,6 +57,7 @@ use tokio::sync::{mpsc, oneshot}; use tracing::{debug, error, trace, warn}; mod active_requests; +mod nat_hole_puncher; mod request_call; mod sessions; mod tests; @@ -67,6 +66,7 @@ use crate::metrics::METRICS; pub use crate::node_info::{NodeAddress, NodeContact}; use active_requests::ActiveRequests; +use nat_hole_puncher::NatHolePuncher; use request_call::RequestCall; use sessions::{Session, Sessions}; @@ -1536,105 +1536,3 @@ impl NatHolePunch for Handler

{ .await) } } - -const DEFAULT_PORT_BIND_TRIES: usize = 4; - -/// Types necessary implement trait [`NatHolePunch`]. -pub struct NatHolePuncher { - /// Ip mode as set in config. - ip_mode: IpMode, - /// This node has been observed to be behind a NAT. - is_behind_nat: Option, - /// The last peer to send us a new peer in a NODES response is stored as the new peer's - /// potential relay until the first request to the new peer after its discovery is either - /// responded or failed. - new_peer_latest_relay: HashMap, - /// Keeps track if this node needs to send a packet to a peer in order to keep a hole punched - /// for it in its NAT. - hole_punch_tracker: HashSetDelay, -} - -impl NatHolePuncher { - fn new(listen_port: u16, local_enr: &Enr, ip_mode: IpMode) -> Self { - let mut nat_hole_puncher = NatHolePuncher { - ip_mode, - is_behind_nat: None, - new_peer_latest_relay: Default::default(), - hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), - }; - // Optimistically only test one advertised socket, ipv4 has precedence. - match ( - local_enr.ip4(), - local_enr.udp4(), - local_enr.ip6(), - local_enr.udp6(), - ) { - (Some(ip), port, _, _) => { - nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip.into()), port); - } - (_, _, Some(ip6), port) => { - nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip6.into()), port); - } - (None, Some(port), _, _) => { - nat_hole_puncher.set_is_behind_nat(listen_port, None, Some(port)); - } - (_, _, None, Some(port)) => { - nat_hole_puncher.set_is_behind_nat(listen_port, None, Some(port)); - } - (None, None, None, None) => {} - } - nat_hole_puncher - } - - fn track(&mut self, peer_socket: SocketAddr) { - if self.is_behind_nat == Some(false) { - return; - } - self.hole_punch_tracker.insert(peer_socket); - } - - // Called when a new observed address is reported at start up or after a - // `Discv5Event::SocketUpdated(socket)` - fn set_is_behind_nat( - &mut self, - listen_port: u16, - observed_ip: Option, - observed_port: Option, - ) { - if Some(listen_port) != observed_port { - self.is_behind_nat = Some(true); - return; - } - // If the node cannot bind to the observed address at any of some random ports, we - // conclude it is behind a NAT. - let Some(ip) = observed_ip else { - return; - }; - let mut rng = rand::thread_rng(); - for _ in 0..DEFAULT_PORT_BIND_TRIES { - let rnd_port: u16 = rng.gen_range(1024..=49151); - let Ok(addr) = format!("{}:{}", ip, rnd_port).parse() else { - return; - }; - if UdpSocket::bind(addr).is_ok() { - self.is_behind_nat = Some(false); - return; - } - } - debug!("Could not bind to any port tried on the observed address {}, setting nodes as behind NAT", ip); - self.is_behind_nat = Some(true); - } -} - -impl Stream for NatHolePuncher { - type Item = Result; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - // Until ip voting is done and an observed public address is finalised, all nodes act as - // if they are behind a NAT. - if self.is_behind_nat == Some(false) || self.hole_punch_tracker.is_empty() { - return Poll::Pending; - } - self.hole_punch_tracker.poll_next_unpin(cx) - } -} diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs new file mode 100644 index 000000000..4d28f6bea --- /dev/null +++ b/src/handler/nat_hole_puncher.rs @@ -0,0 +1,100 @@ +use crate::{node_info::NodeAddress, Enr, IpMode}; +use delay_map::HashSetDelay; +use enr::NodeId; +use futures::{Stream, StreamExt}; +use nat_hole_punch::DEFAULT_HOLE_PUNCH_LIFETIME; +use std::{ + collections::HashMap, + net::{IpAddr, SocketAddr}, + pin::Pin, + task::{Context, Poll}, + time::Duration, +}; + +/// Types necessary implement trait [`NatHolePunch`] on [`Handler`]. +pub(crate) struct NatHolePuncher { + /// Ip mode as set in config. + pub ip_mode: IpMode, + /// This node has been observed to be behind a NAT. + pub is_behind_nat: Option, + /// The last peer to send us a new peer in a NODES response is stored as the new peer's + /// potential relay until the first request to the new peer after its discovery is either + /// responded or failed. + pub new_peer_latest_relay: HashMap, + /// Keeps track if this node needs to send a packet to a peer in order to keep a hole punched + /// for it in its NAT. + pub hole_punch_tracker: HashSetDelay, +} + +impl NatHolePuncher { + pub(crate) fn new(listen_port: u16, local_enr: &Enr, ip_mode: IpMode) -> Self { + let mut nat_hole_puncher = NatHolePuncher { + ip_mode, + is_behind_nat: None, + new_peer_latest_relay: Default::default(), + hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), + }; + // Optimistically only test one advertised socket, ipv4 has precedence. + match ( + local_enr.ip4(), + local_enr.udp4(), + local_enr.ip6(), + local_enr.udp6(), + ) { + (Some(ip), port, _, _) => { + nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip.into()), port); + } + (_, _, Some(ip6), port) => { + nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip6.into()), port); + } + (None, Some(port), _, _) => { + nat_hole_puncher.set_is_behind_nat(listen_port, None, Some(port)); + } + (_, _, None, Some(port)) => { + nat_hole_puncher.set_is_behind_nat(listen_port, None, Some(port)); + } + (None, None, None, None) => {} + } + nat_hole_puncher + } + + pub(crate) fn track(&mut self, peer_socket: SocketAddr) { + if self.is_behind_nat == Some(false) { + return; + } + self.hole_punch_tracker.insert(peer_socket); + } + + // Called when a new observed address is reported at start up or after a + // `Discv5Event::SocketUpdated(socket)` + pub(crate) fn set_is_behind_nat( + &mut self, + listen_port: u16, + observed_ip: Option, + observed_port: Option, + ) { + if Some(listen_port) != observed_port { + self.is_behind_nat = Some(true); + return; + } + // Without and observed IP it is too early to conclude if the local node is behind a NAT, + // return. + let Some(ip) = observed_ip else { + return; + }; + self.is_behind_nat = Some(nat_hole_punch::is_behind_nat(ip, (None, None))); + } +} + +impl Stream for NatHolePuncher { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // Until ip voting is done and an observed public address is finalised, all nodes act as + // if they are behind a NAT. + if self.is_behind_nat == Some(false) || self.hole_punch_tracker.is_empty() { + return Poll::Pending; + } + self.hole_punch_tracker.poll_next_unpin(cx) + } +} From e0522a871382d4d5b9d73b3bc2e2158bb94f9149 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 22 Apr 2023 00:40:28 +0200 Subject: [PATCH 036/154] Update naming to match wire --- src/handler/mod.rs | 8 ++++---- src/handler/sessions/session.rs | 2 +- src/packet/mod.rs | 19 ++++++++++--------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 82e69fe29..309a01702 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -422,12 +422,12 @@ impl Handler

{ ) .await } - PacketKind::Notification { src_id } => { + PacketKind::SessionMessage { src_id } => { let node_address = NodeAddress { socket_addr: inbound_packet.src_address, node_id: src_id, }; - self.handle_notification( + self.handle_session_message( node_address, message_nonce, &inbound_packet.message, @@ -958,8 +958,8 @@ impl Handler

{ } } - /// Handle a notification packet, that is dropped if it can't be decrypted. - async fn handle_notification( + /// Handle a session message packet, that is dropped if it can't be decrypted. + async fn handle_session_message( &mut self, node_address: NodeAddress, // session message sender message_nonce: MessageNonce, diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index 8071b9aa7..3f5a84c51 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -125,7 +125,7 @@ impl Session { let iv: u128 = rand::random(); let header = PacketHeader { message_nonce, - kind: PacketKind::Notification { src_id }, + kind: PacketKind::SessionMessage { src_id }, }; let mut authenticated_data = iv.to_be_bytes().to_vec(); diff --git a/src/packet/mod.rs b/src/packet/mod.rs index db819ee3d..03172b0bd 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -141,10 +141,11 @@ pub enum PacketKind { /// The ENR record of the node if the WHOAREYOU request is out-dated. enr_record: Option, }, - /// A notification. Differs from [`PacketKind::Message`] in the way it handles sessions. A - /// notification packet doesn't trigger a WHOAREYOU response. If a session doesn't exist to + /// A session message is a notification, hence it differs from the [`PacketKind::Message`] in + /// the way it handles sessions since notifications doesn't trigger responses, a session + /// message packet doesn't trigger a WHOAREYOU response. If a session doesn't exist to /// decrypt or encrypt a notification, it is dropped. - Notification { + SessionMessage { /// The sending NodeId. src_id: NodeId, }, @@ -156,7 +157,7 @@ impl From<&PacketKind> for u8 { PacketKind::Message { .. } => 0, PacketKind::WhoAreYou { .. } => 1, PacketKind::Handshake { .. } => 2, - PacketKind::Notification { .. } => 3, + PacketKind::SessionMessage { .. } => 3, } } } @@ -165,7 +166,7 @@ impl PacketKind { /// Encodes the packet type into its corresponding auth_data. pub fn encode(&self) -> Vec { match self { - PacketKind::Message { src_id } | PacketKind::Notification { src_id } => { + PacketKind::Message { src_id } | PacketKind::SessionMessage { src_id } => { src_id.raw().to_vec() } PacketKind::WhoAreYou { id_nonce, enr_seq } => { @@ -291,7 +292,7 @@ impl PacketKind { } let src_id = NodeId::parse(auth_data).map_err(|_| PacketError::InvalidNodeId)?; - Ok(PacketKind::Notification { src_id }) + Ok(PacketKind::SessionMessage { src_id }) } _ => Err(PacketError::UnknownPacket), } @@ -384,7 +385,7 @@ impl Packet { PacketKind::WhoAreYou { .. } => true, PacketKind::Message { .. } | PacketKind::Handshake { .. } - | PacketKind::Notification { .. } => false, + | PacketKind::SessionMessage { .. } => false, } } @@ -392,7 +393,7 @@ impl Packet { /// src_id in this case. pub fn src_id(&self) -> Option { match self.header.kind { - PacketKind::Message { src_id } | PacketKind::Notification { src_id } => Some(src_id), + PacketKind::Message { src_id } | PacketKind::SessionMessage { src_id } => Some(src_id), PacketKind::WhoAreYou { .. } => None, PacketKind::Handshake { src_id, .. } => Some(src_id), } @@ -587,7 +588,7 @@ impl std::fmt::Display for PacketKind { hex::encode(ephem_pubkey), enr_record ), - PacketKind::Notification { src_id } => { + PacketKind::SessionMessage { src_id } => { write!(f, "Notification {{ src_id: {src_id} }}") } } From 2442fa1dda709a437cdf1edde4fdd25c688d938e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 22 Apr 2023 01:09:37 +0200 Subject: [PATCH 037/154] Lint fixes --- src/handler/mod.rs | 27 ++++++++++++--------------- src/handler/sessions/session.rs | 1 + 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 309a01702..342d2a02a 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -481,10 +481,7 @@ impl Handler

{ ); let local_enr = self.enr.read().clone(); let nonce = request_call.packet().header.message_nonce; - match self - .on_time_out(relay, local_enr, nonce, target.into()) - .await - { + match self.on_time_out(relay, local_enr, nonce, target).await { Err(e) => { warn!("Failed to start hole punching. Error: {:?}", e); } @@ -506,7 +503,7 @@ impl Handler

{ request_call.body(), node_address ); - self.send(node_address.clone(), request_call.packet().clone().into()) + self.send(node_address.clone(), request_call.packet().clone()) .await; request_call.increment_retries(); self.active_requests.insert(node_address, request_call); @@ -579,7 +576,7 @@ impl Handler

{ ); // let the filter know we are expecting a response self.add_expected_response(node_address.socket_addr); - self.send(node_address.clone(), packet.into()).await; + self.send(node_address.clone(), packet).await; self.active_requests.insert(node_address, call); Ok(()) @@ -597,7 +594,7 @@ impl Handler

{ return; } }; - self.send(node_address, packet.into()).await; + self.send(node_address, packet).await; } else { // Either the session is being established or has expired. We simply drop the // response in this case. @@ -637,7 +634,7 @@ impl Handler

{ .expect("Must be the correct challenge size"); debug!("Sending WHOAREYOU to {}", node_address); self.add_expected_response(node_address.socket_addr); - self.send(node_address.clone(), packet.into()).await; + self.send(node_address.clone(), packet).await; self.active_challenges.insert( node_address, Challenge { @@ -766,7 +763,7 @@ impl Handler

{ // Reinsert the request_call self.insert_active_request(request_call); // Send the actual packet to the send task. - self.send(node_address.clone(), auth_packet.into()).await; + self.send(node_address.clone(), auth_packet).await; // Notify the application that the session has been established self.new_connection(enr, node_address.socket_addr, connection_direction) @@ -782,7 +779,7 @@ impl Handler

{ request_call.set_handshake_sent(); // Reinsert the request_call self.insert_active_request(request_call); - self.send(node_address.clone(), auth_packet.into()).await; + self.send(node_address.clone(), auth_packet).await; let id = RequestId::random(); let request = RequestBody::FindNode { distances: vec![0] }; @@ -1379,7 +1376,7 @@ impl Handler

{ return Err(HolePunchError::RelayError(e)); } }; - self.send(tgt_node_address, packet.into()).await; + self.send(tgt_node_address, packet).await; Ok(()) } else { // Either the session is being established or has expired. We simply drop the @@ -1448,7 +1445,7 @@ impl NatHolePunch for Handler

{ return Err(HolePunchError::RelayError(e)); } }; - self.send(relay, packet.into()).await; + self.send(relay, packet).await; } else { // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays // within the time out of the udp entrypoint for the target peer in the initiator's @@ -1531,8 +1528,8 @@ impl NatHolePunch for Handler

{ &mut self, dst: SocketAddr, ) -> Result<(), HolePunchError> { - Ok(self - .send_outbound(dst, Outbound::KeepHolePunched(dst)) - .await) + self.send_outbound(dst, Outbound::KeepHolePunched(dst)) + .await; + Ok(()) } } diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index 3f5a84c51..781ad9544 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -182,6 +182,7 @@ impl Session { /// Generates session keys from an authentication header. If the IP of the ENR does not match the /// source IP address, we consider this session untrusted. The output returns a boolean which /// specifies if the Session is trusted or not. + #[allow(clippy::too_many_arguments)] pub(crate) fn establish_from_challenge( local_key: Arc>, local_id: &NodeId, From b18b411d4b09af539c86d2fa5860cc263f9608bd Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 22 Apr 2023 01:17:54 +0200 Subject: [PATCH 038/154] Doc links fix --- .github/workflows/build.yml | 2 +- src/handler/mod.rs | 2 +- src/handler/nat_hole_puncher.rs | 3 ++- src/ipmode.rs | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7df3ddd6d..9d4b98240 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,4 +54,4 @@ jobs: - name: Get latest version of stable rust run: rustup update stable - name: Check rustdoc links - run: RUSTDOCFLAGS="--deny broken_intra_doc_links" cargo doc --verbose --workspace --no-deps --document-private-items + run: RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --verbose --workspace --no-deps --document-private-items diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 342d2a02a..4ed618ecc 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -105,7 +105,7 @@ pub enum HandlerIn { /// be returned here to submit the application's response. WhoAreYou(WhoAreYouRef, Option), - /// Response to a [`HandlerOut::FindEnrForHolePunch`]. Returns the ENR if it was found. + /// Response to a [`HandlerOut::FindHolePunchEnr`]. Returns the ENR if it was found. HolePunchEnr(Option, RelayMsg), /// Observed socket has been update. The old socket and the current socket. diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index 4d28f6bea..c3758e7fd 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -11,7 +11,8 @@ use std::{ time::Duration, }; -/// Types necessary implement trait [`NatHolePunch`] on [`Handler`]. +/// Types necessary implement trait [`nat_hole_punch::NatHolePunch`] on +/// [`crate::handler::Handler`]. pub(crate) struct NatHolePuncher { /// Ip mode as set in config. pub ip_mode: IpMode, diff --git a/src/ipmode.rs b/src/ipmode.rs index cf527d8fa..b71ea5705 100644 --- a/src/ipmode.rs +++ b/src/ipmode.rs @@ -232,7 +232,7 @@ mod tests { } } -/// Copied from the standard library. See https://github.com/rust-lang/rust/issues/27709 +/// Copied from the standard library. See /// The current code is behind the `ip` feature. pub const fn to_ipv4_mapped(ip: &std::net::Ipv6Addr) -> Option { match ip.octets() { From 36dcfee154cae0f4f3122a9cb18de7e9f9fa8c01 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 22 Apr 2023 13:55:03 +0200 Subject: [PATCH 039/154] Bug fix --- src/handler/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 4ed618ecc..63d6166d7 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1009,6 +1009,8 @@ impl Handler

{ // Sessions could be awaiting an ENR response. Check if this response matches // these let Some(request_id) = session.awaiting_enr.as_ref() else { + // Handle standard responses + self.handle_response(node_address, response).await; return; }; if &response.id == request_id { From eae9b14c0207f6c7df996f6d782aaac84b33f648 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 25 Apr 2023 18:23:20 +0200 Subject: [PATCH 040/154] Log messages fixes --- src/lru_time_cache.rs | 2 +- src/packet/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lru_time_cache.rs b/src/lru_time_cache.rs index 1248f19d5..ae2a72370 100644 --- a/src/lru_time_cache.rs +++ b/src/lru_time_cache.rs @@ -95,7 +95,7 @@ impl LruTimeCache { let v = self.map.remove(key).map(|v| v.0); if let Some(ref mut tx) = self.tx { if let Err(e) = tx.try_send(key.clone()) { - warn!("remove failed, {}", e); + warn!("failed to notify of remove, {}", e); } } v diff --git a/src/packet/mod.rs b/src/packet/mod.rs index 03172b0bd..f6308e503 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -589,7 +589,7 @@ impl std::fmt::Display for PacketKind { enr_record ), PacketKind::SessionMessage { src_id } => { - write!(f, "Notification {{ src_id: {src_id} }}") + write!(f, "SessionMessage {{ src_id: {src_id} }}") } } } From 39cd9a316cf95f340f98dc5d7c2b39b45ba4c0c6 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 26 Apr 2023 14:01:41 +0200 Subject: [PATCH 041/154] Updates from nat hole punch crate --- Cargo.toml | 4 ++-- src/handler/mod.rs | 42 ++++++++++++++++----------------- src/handler/nat_hole_puncher.rs | 2 +- src/socket/recv.rs | 3 +-- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ea4207d1c..132487b3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ categories = ["network-programming", "asynchronous"] exclude = [".gitignore", ".github/*"] [dependencies] -nat_hole_punch = { package = "nat_hole_punch", git = "https://github.com/emhane/nat_hole_punch", branch = "nat-hole-punch-discv5.2" } -enr = { version = "0.7.0", features = ["k256", "ed25519"] } +nat_hole_punch = { package = "nat_hole_punch", git = "https://github.com/emhane/nat_hole_punch", branch = "main" } +enr = { version = "0.8.0", features = ["k256", "ed25519"] } tokio = { version = "1.15.0", features = ["net", "sync", "macros", "rt"] } tokio-stream = "0.1.8" tokio-util = { version = "0.6.9", features = ["time"] } diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 63d6166d7..f0d191bb9 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -106,7 +106,7 @@ pub enum HandlerIn { WhoAreYou(WhoAreYouRef, Option), /// Response to a [`HandlerOut::FindHolePunchEnr`]. Returns the ENR if it was found. - HolePunchEnr(Option, RelayMsg), + HolePunchEnr(Option, RelayMsg), /// Observed socket has been update. The old socket and the current socket. SocketUpdate(Option, SocketAddr), @@ -140,7 +140,7 @@ pub enum HandlerOut { /// A peer has supposed we have passed it another peer in a NODES response, if that is true /// (very probably not false) the ENR of that peer is returned in a /// [`HandlerIn::HolePunchEnr`]. - FindHolePunchEnr(NodeId, RelayMsg), + FindHolePunchEnr(NodeId, RelayMsg), } /// How we connected to the node. @@ -481,7 +481,10 @@ impl Handler

{ ); let local_enr = self.enr.read().clone(); let nonce = request_call.packet().header.message_nonce; - match self.on_time_out(relay, local_enr, nonce, target).await { + match self + .on_request_time_out(relay, local_enr, nonce, target) + .await + { Err(e) => { warn!("Failed to start hole punching. Error: {:?}", e); } @@ -1353,7 +1356,7 @@ impl Handler

{ async fn on_hole_punch_tgt_enr( &mut self, tgt_enr: Option, - relay_msg_notif: RelayMsg, + relay_msg_notif: RelayMsg, ) -> Result<(), HolePunchError> { let Some(tgt_enr) = tgt_enr else { return Err(HolePunchError::RelayError(Discv5Error::Custom("Target enr not found"))); @@ -1413,16 +1416,15 @@ impl Handler

{ #[async_trait] impl NatHolePunch for Handler

{ - type TEnr = Enr; - type TNodeAddress = NodeAddress; - type TDiscv5Error = Discv5Error; - async fn on_time_out( + type SessionIndex = NodeAddress; + type Discv5Error = Discv5Error; + async fn on_request_time_out( &mut self, - relay: Self::TNodeAddress, - local_enr: Self::TEnr, + relay: Self::SessionIndex, + local_enr: Enr, timed_out_message_nonce: MessageNonce, - target_session_index: Self::TNodeAddress, - ) -> Result<(), HolePunchError> { + target_session_index: Self::SessionIndex, + ) -> Result<(), HolePunchError> { // Another hole punch process with this target may have just completed. if self.sessions.cache.get(&target_session_index).is_some() { return Ok(()); @@ -1430,7 +1432,7 @@ impl NatHolePunch for Handler

{ if let Some(session) = self.sessions.cache.get_mut(&relay) { let relay_init_notif = RelayInit( local_enr, - target_session_index.node_id.raw(), + target_session_index.node_id, timed_out_message_nonce, ); trace!( @@ -1463,18 +1465,16 @@ impl NatHolePunch for Handler

{ async fn on_relay_init( &mut self, - notif: RelayInit, - ) -> Result<(), HolePunchError> { + notif: RelayInit, + ) -> Result<(), HolePunchError> { let RelayInit(initiator, tgt, nonce) = notif; // Assemble the notification for the target let relay_msg_notif = RelayMsg(initiator, nonce); // Check for target peer in our kbuckets otherwise drop notification. - let tgt_node_id = NodeId::new(&tgt); - if let Err(e) = self .service_send - .send(HandlerOut::FindHolePunchEnr(tgt_node_id, relay_msg_notif)) + .send(HandlerOut::FindHolePunchEnr(tgt, relay_msg_notif)) .await { return Err(HolePunchError::RelayError(e.into())); @@ -1484,8 +1484,8 @@ impl NatHolePunch for Handler

{ async fn on_relay_msg( &mut self, - notif: RelayMsg, - ) -> Result<(), HolePunchError> { + notif: RelayMsg, + ) -> Result<(), HolePunchError> { let RelayMsg(initiator, nonce) = notif; let initiator_node_address = @@ -1529,7 +1529,7 @@ impl NatHolePunch for Handler

{ async fn on_hole_punch_expired( &mut self, dst: SocketAddr, - ) -> Result<(), HolePunchError> { + ) -> Result<(), HolePunchError> { self.send_outbound(dst, Outbound::KeepHolePunched(dst)) .await; Ok(()) diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index c3758e7fd..8b676377f 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -83,7 +83,7 @@ impl NatHolePuncher { let Some(ip) = observed_ip else { return; }; - self.is_behind_nat = Some(nat_hole_punch::is_behind_nat(ip, (None, None))); + self.is_behind_nat = Some(nat_hole_punch::is_behind_nat(ip, None, None)); } } diff --git a/src/socket/recv.rs b/src/socket/recv.rs index d973e22e2..c1d42e679 100644 --- a/src/socket/recv.rs +++ b/src/socket/recv.rs @@ -6,7 +6,6 @@ use super::filter::{Filter, FilterConfig}; use crate::{ ipmode::to_ipv4_mapped, metrics::METRICS, node_info::NodeAddress, packet::*, Executor, }; -use nat_hole_punch; use parking_lot::RwLock; use std::{ collections::HashMap, @@ -152,7 +151,7 @@ impl RecvHandler { Err(e) => { // This could be a packet to keep a NAT hole punched for this node in the // sender's NAT, hence only serves purpose for the sender. - if nat_hole_punch::is_keep_hole_punched_packet(length) { + if length == 0 { debug!("Appears to be a packet to keep a hole punched in sender's NAT, dropping. src: {}", src_address); } else { // Could not decode the packet, drop it. From 5e882574a413e8a232df040b68cbc54de83b8669 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 27 Apr 2023 08:29:27 +0200 Subject: [PATCH 042/154] fixup! Updates from nat hole punch crate --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 132487b3b..19c38ed72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ exclude = [".gitignore", ".github/*"] [dependencies] nat_hole_punch = { package = "nat_hole_punch", git = "https://github.com/emhane/nat_hole_punch", branch = "main" } -enr = { version = "0.8.0", features = ["k256", "ed25519"] } +enr = { version = "0.7.0", features = ["k256", "ed25519"] } tokio = { version = "1.15.0", features = ["net", "sync", "macros", "rt"] } tokio-stream = "0.1.8" tokio-util = { version = "0.6.9", features = ["time"] } From b68beaa6c60614b1d7e3d7624f465b50255b649c Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 30 Apr 2023 12:44:21 +0200 Subject: [PATCH 043/154] Incorporate notification --- Cargo.toml | 1 + src/lib.rs | 1 + src/macro_rules.rs | 42 ++ src/rpc.rs | 1009 ---------------------------- src/rpc/mod.rs | 468 +++++++++++++ src/rpc/notification/mod.rs | 63 ++ src/rpc/notification/relay_init.rs | 75 +++ src/rpc/notification/relay_msg.rs | 60 ++ src/rpc/request/mod.rs | 166 +++++ src/rpc/request/request_body.rs | 37 + src/rpc/response/mod.rs | 183 +++++ src/rpc/response/response_body.rs | 54 ++ src/service.rs | 22 +- 13 files changed, 1155 insertions(+), 1026 deletions(-) create mode 100644 src/macro_rules.rs delete mode 100644 src/rpc.rs create mode 100644 src/rpc/mod.rs create mode 100644 src/rpc/notification/mod.rs create mode 100644 src/rpc/notification/relay_init.rs create mode 100644 src/rpc/notification/relay_msg.rs create mode 100644 src/rpc/request/mod.rs create mode 100644 src/rpc/request/request_body.rs create mode 100644 src/rpc/response/mod.rs create mode 100644 src/rpc/response/response_body.rs diff --git a/Cargo.toml b/Cargo.toml index 19c38ed72..7a7053650 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ more-asserts = "0.2.2" async-trait = "0.1.67" tokio-context = "0.1.3" mio = "0.8.6" +parse-display-derive = "0.8.0" [dev-dependencies] rand_07 = { package = "rand", version = "0.7" } diff --git a/src/lib.rs b/src/lib.rs index 72d1e5308..c38293a6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,6 +110,7 @@ pub mod handler; mod ipmode; pub mod kbucket; mod lru_time_cache; +pub mod macro_rules; pub mod metrics; mod node_info; pub mod packet; diff --git a/src/macro_rules.rs b/src/macro_rules.rs new file mode 100644 index 000000000..9c24c2891 --- /dev/null +++ b/src/macro_rules.rs @@ -0,0 +1,42 @@ +/// Implements the From trait for an enum from some type. Nests a type in a variant with a single +/// value. Takes any generics, the type to nest, the enum and the variant. +#[macro_export] +macro_rules! impl_from_variant_wrap { + ($(<$($generic: ident$(: $trait: ident$(+ $traits: ident)*)*,)+>)*, $from_type: ty, $to_enum: ty, $to_variant: path) => { + impl$(<$($generic $(: $trait $(+ $traits)*)*,)+>)* From<$from_type> for $to_enum { + fn from(t: $from_type) -> Self { + $to_variant(t) + } + } + }; +} + +/// Implements the From trait for some type from an enum. Extracts a type nested in a variant with +/// a single value. Takes any generics, the enum, the type nested in the variant and the variant. +#[macro_export] +macro_rules! impl_from_variant_unwrap { + ($(<$($generic: ident$(: $trait: ident$(+ $traits: ident)*)*,)+>)*, $from_enum: ty, $to_type: ty, $from_variant: path) => { + impl$(<$($generic $(: $trait $(+ $traits)*)*,)+>)* From<$from_enum> for $to_type { + fn from(e: $from_enum) -> Self { + if let $from_variant(v) = e { + return v; + } + panic!("Bad impl of From") + } + } + }; +} + +/// Implements the From trait for some type from a (1-)tuple struct. Extracts a type nested in a +/// 1-tuple tuple struct. Takes any generics, the tuple struct type and the type nested in the +/// tuple struct. +#[macro_export] +macro_rules! impl_from_tuple_struct_unwrap { + ($(<$($generic: ident$(: $trait: ident$(+ $traits: ident)*)*,)+>)*, $from_tuple_struct: ty, $to_type: ty) => { + impl$(<$($generic $(: $trait $(+ $traits)*)*,)+>)* From<$from_tuple_struct> for $to_type { + fn from(s: $from_tuple_struct) -> Self { + s.0 + } + } + }; +} diff --git a/src/rpc.rs b/src/rpc.rs deleted file mode 100644 index a4f9fe7f4..000000000 --- a/src/rpc.rs +++ /dev/null @@ -1,1009 +0,0 @@ -use enr::{CombinedKey, Enr}; -use rlp::{DecoderError, RlpStream}; -use std::net::{IpAddr, Ipv6Addr}; -use tracing::{debug, warn}; - -type TopicHash = [u8; 32]; - -/// Type to manage the request IDs. -#[derive(Debug, Clone, PartialEq, Hash, Eq)] -pub struct RequestId(pub Vec); - -impl From for Vec { - fn from(id: RequestId) -> Self { - id.0 - } -} - -impl RequestId { - /// Decodes the ID from a raw bytes. - pub fn decode(data: Vec) -> Result { - if data.len() > 8 { - return Err(DecoderError::Custom("Invalid ID length")); - } - Ok(RequestId(data)) - } - - pub fn random() -> Self { - let rand: u64 = rand::random(); - RequestId(rand.to_be_bytes().to_vec()) - } - - pub fn as_bytes(&self) -> &[u8] { - &self.0 - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -/// A combined type representing requests and responses. -pub enum Message { - /// A request, which contains its [`RequestId`]. - Request(Request), - /// A Response, which contains the [`RequestId`] of its associated request. - Response(Response), -} - -#[derive(Debug, Clone, PartialEq, Eq)] -/// A request sent between nodes. -pub struct Request { - /// The [`RequestId`] of the request. - pub id: RequestId, - /// The body of the request. - pub body: RequestBody, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -/// A response sent in response to a [`Request`] -pub struct Response { - /// The [`RequestId`] of the request that triggered this response. - pub id: RequestId, - /// The body of this response. - pub body: ResponseBody, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum RequestBody { - /// A PING request. - Ping { - /// Our current ENR sequence number. - enr_seq: u64, - }, - /// A FINDNODE request. - FindNode { - /// The distance(s) of peers we expect to be returned in the response. - distances: Vec, - }, - /// A Talk request. - Talk { - /// The protocol requesting. - protocol: Vec, - /// The request. - request: Vec, - }, - /// A REGISTERTOPIC request. - RegisterTopic { - topic: Vec, - enr: crate::Enr, - ticket: Vec, - }, - /// A TOPICQUERY request. - TopicQuery { topic: TopicHash }, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ResponseBody { - /// A PONG response. - Pong { - /// The current ENR sequence number of the responder. - enr_seq: u64, - /// Our external IP address as observed by the responder. - ip: IpAddr, - /// Our external UDP port as observed by the responder. - port: u16, - }, - /// A NODES response. - Nodes { - /// The total number of responses that make up this response. - total: u64, - /// A list of ENR's returned by the responder. - nodes: Vec>, - }, - /// The TALK response. - Talk { - /// The response for the talk. - response: Vec, - }, - Ticket { - ticket: Vec, - wait_time: u64, - }, - RegisterConfirmation { - topic: Vec, - }, -} - -impl Request { - pub fn msg_type(&self) -> u8 { - match self.body { - RequestBody::Ping { .. } => 1, - RequestBody::FindNode { .. } => 3, - RequestBody::Talk { .. } => 5, - RequestBody::RegisterTopic { .. } => 7, - RequestBody::TopicQuery { .. } => 10, - } - } - - /// Encodes a Message to RLP-encoded bytes. - pub fn encode(self) -> Vec { - let mut buf = Vec::with_capacity(10); - let msg_type = self.msg_type(); - buf.push(msg_type); - let id = &self.id; - match self.body { - RequestBody::Ping { enr_seq } => { - let mut s = RlpStream::new(); - s.begin_list(2); - s.append(&id.as_bytes()); - s.append(&enr_seq); - buf.extend_from_slice(&s.out()); - buf - } - RequestBody::FindNode { distances } => { - let mut s = RlpStream::new(); - s.begin_list(2); - s.append(&id.as_bytes()); - s.begin_list(distances.len()); - for distance in distances { - s.append(&distance); - } - buf.extend_from_slice(&s.out()); - buf - } - RequestBody::Talk { protocol, request } => { - let mut s = RlpStream::new(); - s.begin_list(3); - s.append(&id.as_bytes()); - s.append(&protocol); - s.append(&request); - buf.extend_from_slice(&s.out()); - buf - } - RequestBody::RegisterTopic { topic, enr, ticket } => { - let mut s = RlpStream::new(); - s.begin_list(4); - s.append(&id.as_bytes()); - s.append(&topic); - s.append(&enr); - s.append(&ticket); - buf.extend_from_slice(&s.out()); - buf - } - RequestBody::TopicQuery { topic } => { - let mut s = RlpStream::new(); - s.begin_list(2); - s.append(&id.as_bytes()); - s.append(&(&topic as &[u8])); - buf.extend_from_slice(&s.out()); - buf - } - } - } -} - -impl Response { - pub fn msg_type(&self) -> u8 { - match &self.body { - ResponseBody::Pong { .. } => 2, - ResponseBody::Nodes { .. } => 4, - ResponseBody::Talk { .. } => 6, - ResponseBody::Ticket { .. } => 8, - ResponseBody::RegisterConfirmation { .. } => 9, - } - } - - /// Determines if the response is a valid response to the given request. - pub fn match_request(&self, req: &RequestBody) -> bool { - match self.body { - ResponseBody::Pong { .. } => matches!(req, RequestBody::Ping { .. }), - ResponseBody::Nodes { .. } => { - matches!( - req, - RequestBody::FindNode { .. } | RequestBody::TopicQuery { .. } - ) - } - ResponseBody::Talk { .. } => matches!(req, RequestBody::Talk { .. }), - ResponseBody::Ticket { .. } => matches!(req, RequestBody::RegisterTopic { .. }), - ResponseBody::RegisterConfirmation { .. } => { - matches!(req, RequestBody::RegisterTopic { .. }) - } - } - } - - /// Encodes a Message to RLP-encoded bytes. - pub fn encode(self) -> Vec { - let mut buf = Vec::with_capacity(10); - let msg_type = self.msg_type(); - buf.push(msg_type); - let id = &self.id; - match self.body { - ResponseBody::Pong { enr_seq, ip, port } => { - let mut s = RlpStream::new(); - s.begin_list(4); - s.append(&id.as_bytes()); - s.append(&enr_seq); - match ip { - IpAddr::V4(addr) => s.append(&(&addr.octets() as &[u8])), - IpAddr::V6(addr) => s.append(&(&addr.octets() as &[u8])), - }; - s.append(&port); - buf.extend_from_slice(&s.out()); - buf - } - ResponseBody::Nodes { total, nodes } => { - let mut s = RlpStream::new(); - s.begin_list(3); - s.append(&id.as_bytes()); - s.append(&total); - - if nodes.is_empty() { - s.begin_list(0); - } else { - s.begin_list(nodes.len()); - for node in nodes { - s.append(&node); - } - } - buf.extend_from_slice(&s.out()); - buf - } - ResponseBody::Talk { response } => { - let mut s = RlpStream::new(); - s.begin_list(2); - s.append(&id.as_bytes()); - s.append(&response); - buf.extend_from_slice(&s.out()); - buf - } - ResponseBody::Ticket { ticket, wait_time } => { - let mut s = RlpStream::new(); - s.begin_list(3); - s.append(&id.as_bytes()); - s.append(&ticket); - s.append(&wait_time); - buf.extend_from_slice(&s.out()); - buf - } - ResponseBody::RegisterConfirmation { topic } => { - let mut s = RlpStream::new(); - s.begin_list(2); - s.append(&id.as_bytes()); - s.append(&topic); - buf.extend_from_slice(&s.out()); - buf - } - } - } -} - -impl std::fmt::Display for RequestId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", hex::encode(&self.0)) - } -} - -impl std::fmt::Display for Message { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Message::Request(request) => write!(f, "{request}"), - Message::Response(response) => write!(f, "{response}"), - } - } -} - -impl std::fmt::Display for Response { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Response: id: {}: {}", self.id, self.body) - } -} - -impl std::fmt::Display for ResponseBody { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ResponseBody::Pong { enr_seq, ip, port } => { - write!(f, "PONG: Enr-seq: {enr_seq}, Ip: {ip:?}, Port: {port}") - } - ResponseBody::Nodes { total, nodes } => { - write!(f, "NODES: total: {total}, Nodes: [")?; - let mut first = true; - for id in nodes { - if !first { - write!(f, ", {id}")?; - } else { - write!(f, "{id}")?; - } - first = false; - } - - write!(f, "]") - } - ResponseBody::Talk { response } => { - write!(f, "Response: Response {}", hex::encode(response)) - } - ResponseBody::Ticket { ticket, wait_time } => { - write!(f, "TICKET: Ticket: {ticket:?}, Wait time: {wait_time}") - } - ResponseBody::RegisterConfirmation { topic } => { - write!(f, "REGTOPIC: Registered: {}", hex::encode(topic)) - } - } - } -} - -impl std::fmt::Display for Request { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Request: id: {}: {}", self.id, self.body) - } -} - -impl std::fmt::Display for RequestBody { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - RequestBody::Ping { enr_seq } => write!(f, "PING: enr_seq: {enr_seq}"), - RequestBody::FindNode { distances } => { - write!(f, "FINDNODE Request: distance: {distances:?}") - } - RequestBody::Talk { protocol, request } => write!( - f, - "TALK: protocol: {}, request: {}", - hex::encode(protocol), - hex::encode(request) - ), - RequestBody::TopicQuery { topic } => write!(f, "TOPICQUERY: topic: {topic:?}"), - RequestBody::RegisterTopic { topic, enr, ticket } => write!( - f, - "RegisterTopic: topic: {}, enr: {}, ticket: {}", - hex::encode(topic), - enr.to_base64(), - hex::encode(ticket) - ), - } - } -} -#[allow(dead_code)] -impl Message { - pub fn encode(self) -> Vec { - match self { - Self::Request(request) => request.encode(), - Self::Response(response) => response.encode(), - } - } - - pub fn decode(data: &[u8]) -> Result { - if data.len() < 3 { - return Err(DecoderError::RlpIsTooShort); - } - - let msg_type = data[0]; - - let rlp = rlp::Rlp::new(&data[1..]); - - let list_len = rlp.item_count().and_then(|size| { - if size < 2 { - Err(DecoderError::RlpIncorrectListLen) - } else { - Ok(size) - } - })?; - - let id = RequestId::decode(rlp.val_at::>(0)?)?; - - let message = match msg_type { - 1 => { - // PingRequest - if list_len != 2 { - debug!( - "Ping Request has an invalid RLP list length. Expected 2, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - Message::Request(Request { - id, - body: RequestBody::Ping { - enr_seq: rlp.val_at::(1)?, - }, - }) - } - 2 => { - // PingResponse - if list_len != 4 { - debug!( - "Ping Response has an invalid RLP list length. Expected 4, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - let ip_bytes = rlp.val_at::>(2)?; - let ip = match ip_bytes.len() { - 4 => { - let mut ip = [0u8; 4]; - ip.copy_from_slice(&ip_bytes); - IpAddr::from(ip) - } - 16 => { - let mut ip = [0u8; 16]; - ip.copy_from_slice(&ip_bytes); - let ipv6 = Ipv6Addr::from(ip); - // If the ipv6 is ipv4 compatible/mapped, simply return the ipv4. - if let Some(ipv4) = ipv6.to_ipv4() { - IpAddr::V4(ipv4) - } else { - IpAddr::V6(ipv6) - } - } - _ => { - debug!("Ping Response has incorrect byte length for IP"); - return Err(DecoderError::RlpIncorrectListLen); - } - }; - let port = rlp.val_at::(3)?; - Message::Response(Response { - id, - body: ResponseBody::Pong { - enr_seq: rlp.val_at::(1)?, - ip, - port, - }, - }) - } - 3 => { - // FindNodeRequest - if list_len != 2 { - debug!( - "FindNode Request has an invalid RLP list length. Expected 2, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - let distances = rlp.list_at::(1)?; - - for distance in distances.iter() { - if distance > &256u64 { - warn!( - "Rejected FindNode request asking for unknown distance {}, maximum 256", - distance - ); - return Err(DecoderError::Custom("FINDNODE request distance invalid")); - } - } - - Message::Request(Request { - id, - body: RequestBody::FindNode { distances }, - }) - } - 4 => { - // NodesResponse - if list_len != 3 { - debug!( - "Nodes Response has an invalid RLP list length. Expected 3, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - - let nodes = { - let enr_list_rlp = rlp.at(2)?; - if enr_list_rlp.is_empty() { - // no records - vec![] - } else { - enr_list_rlp.as_list::>()? - } - }; - Message::Response(Response { - id, - body: ResponseBody::Nodes { - total: rlp.val_at::(1)?, - nodes, - }, - }) - } - 5 => { - // Talk Request - if list_len != 3 { - debug!( - "Talk Request has an invalid RLP list length. Expected 3, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - let protocol = rlp.val_at::>(1)?; - let request = rlp.val_at::>(2)?; - Message::Request(Request { - id, - body: RequestBody::Talk { protocol, request }, - }) - } - 6 => { - // Talk Response - if list_len != 2 { - debug!( - "Talk Response has an invalid RLP list length. Expected 2, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - let response = rlp.val_at::>(1)?; - Message::Response(Response { - id, - body: ResponseBody::Talk { response }, - }) - } - _ => { - return Err(DecoderError::Custom("Unknown RPC message type")); - } /* - * All other RPC messages are currently not supported as per the 5.1 specification. - - 7 => { - // RegisterTopicRequest - if list_len != 2 { - debug!("RegisterTopic Request has an invalid RLP list length. Expected 2, found {}", list_len); - return Err(DecoderError::RlpIncorrectListLen); - } - let ticket = rlp.val_at::>(1)?; - Message::Request(Request { - id, - body: RequestBody::RegisterTopic { ticket }, - }) - } - 8 => { - // RegisterTopicResponse - if list_len != 2 { - debug!("RegisterTopic Response has an invalid RLP list length. Expected 2, found {}", list_len); - return Err(DecoderError::RlpIncorrectListLen); - } - Message::Response(Response { - id, - body: ResponseBody::RegisterTopic { - registered: rlp.val_at::(1)?, - }, - }) - } - 9 => { - // TopicQueryRequest - if list_len != 2 { - debug!( - "TopicQuery Request has an invalid RLP list length. Expected 2, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - let topic = { - let topic_bytes = rlp.val_at::>(1)?; - if topic_bytes.len() > 32 { - debug!("Ticket Request has a topic greater than 32 bytes"); - return Err(DecoderError::RlpIsTooBig); - } - let mut topic = [0u8; 32]; - topic[32 - topic_bytes.len()..].copy_from_slice(&topic_bytes); - topic - }; - Message::Request(Request { - id, - body: RequestBody::TopicQuery { topic }, - }) - } - */ - }; - - Ok(message) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use enr::EnrBuilder; - - #[test] - fn ref_test_encode_request_ping() { - // reference input - let id = RequestId(vec![1]); - let enr_seq = 1; - let message = Message::Request(Request { - id, - body: RequestBody::Ping { enr_seq }, - }); - - // expected hex output - let expected_output = hex::decode("01c20101").unwrap(); - - dbg!(hex::encode(message.clone().encode())); - assert_eq!(message.encode(), expected_output); - } - - #[test] - fn ref_test_encode_request_findnode() { - // reference input - let id = RequestId(vec![1]); - let distances = vec![256]; - let message = Message::Request(Request { - id, - body: RequestBody::FindNode { distances }, - }); - - // expected hex output - let expected_output = hex::decode("03c501c3820100").unwrap(); - dbg!(hex::encode(message.clone().encode())); - - assert_eq!(message.encode(), expected_output); - } - - #[test] - fn ref_test_encode_response_ping() { - // reference input - let id = RequestId(vec![1]); - let enr_seq = 1; - let ip: IpAddr = "127.0.0.1".parse().unwrap(); - let port = 5000; - let message = Message::Response(Response { - id, - body: ResponseBody::Pong { enr_seq, ip, port }, - }); - - // expected hex output - let expected_output = hex::decode("02ca0101847f000001821388").unwrap(); - - dbg!(hex::encode(message.clone().encode())); - assert_eq!(message.encode(), expected_output); - } - - #[test] - fn ref_test_encode_response_nodes_empty() { - // reference input - let id = RequestId(vec![1]); - let total = 1; - - // expected hex output - let expected_output = hex::decode("04c30101c0").unwrap(); - - let message = Message::Response(Response { - id, - body: ResponseBody::Nodes { - total, - nodes: vec![], - }, - }); - assert_eq!(message.encode(), expected_output); - } - - #[test] - fn ref_test_encode_response_nodes() { - // reference input - let id = RequestId(vec![1]); - let total = 1; - - let enr = "-HW4QCjfjuCfSmIJHxqLYfGKrSz-Pq3G81DVJwd_muvFYJiIOkf0bGtJu7kZVCOPnhSTMneyvR4MRbF3G5TNB4wy2ssBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg".parse::>().unwrap(); - // expected hex output - let expected_output = hex::decode("04f87b0101f877f875b84028df8ee09f4a62091f1a8b61f18aad2cfe3eadc6f350d527077f9aebc56098883a47f46c6b49bbb91954238f9e14933277b2bd1e0c45b1771b94cd078c32dacb0182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138").unwrap(); - - let message = Message::Response(Response { - id, - body: ResponseBody::Nodes { - total, - nodes: vec![enr], - }, - }); - dbg!(hex::encode(message.clone().encode())); - assert_eq!(message.encode(), expected_output); - } - - #[test] - fn ref_test_encode_response_nodes_multiple() { - // reference input - let id = RequestId(vec![1]); - let total = 1; - let enr = "enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg".parse::>().unwrap(); - - let enr2 = "enr:-HW4QNfxw543Ypf4HXKXdYxkyzfcxcO-6p9X986WldfVpnVTQX1xlTnWrktEWUbeTZnmgOuAY_KUhbVV1Ft98WoYUBMBgmlkgnY0iXNlY3AyNTZrMaEDDiy3QkHAxPyOgWbxp5oF1bDdlYE6dLCUUp8xfVw50jU".parse::>().unwrap(); - - // expected hex output - let expected_output = hex::decode("04f8f20101f8eef875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235").unwrap(); - - let message = Message::Response(Response { - id, - body: ResponseBody::Nodes { - total, - nodes: vec![enr, enr2], - }, - }); - dbg!(hex::encode(message.clone().encode())); - assert_eq!(message.encode(), expected_output); - } - - #[test] - fn ref_decode_response_nodes_multiple() { - let input = hex::decode("04f8f20101f8eef875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235").unwrap(); - - let expected_enr1 = "enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg".parse::>().unwrap(); - let expected_enr2 = "enr:-HW4QNfxw543Ypf4HXKXdYxkyzfcxcO-6p9X986WldfVpnVTQX1xlTnWrktEWUbeTZnmgOuAY_KUhbVV1Ft98WoYUBMBgmlkgnY0iXNlY3AyNTZrMaEDDiy3QkHAxPyOgWbxp5oF1bDdlYE6dLCUUp8xfVw50jU".parse::>().unwrap(); - - let decoded = Message::decode(&input).unwrap(); - - match decoded { - Message::Response(response) => match response.body { - ResponseBody::Nodes { total, nodes } => { - assert_eq!(total, 1); - assert_eq!(nodes[0], expected_enr1); - assert_eq!(nodes[1], expected_enr2); - } - _ => panic!("Invalid decoding"), - }, - _ => panic!("Invalid decoding"), - } - } - - #[test] - fn encode_decode_ping_request() { - let id = RequestId(vec![1]); - let request = Message::Request(Request { - id, - body: RequestBody::Ping { enr_seq: 15 }, - }); - - let encoded = request.clone().encode(); - let decoded = Message::decode(&encoded).unwrap(); - - assert_eq!(request, decoded); - } - - #[test] - fn encode_decode_ping_response() { - let id = RequestId(vec![1]); - let request = Message::Response(Response { - id, - body: ResponseBody::Pong { - enr_seq: 15, - ip: "127.0.0.1".parse().unwrap(), - port: 80, - }, - }); - - let encoded = request.clone().encode(); - let decoded = Message::decode(&encoded).unwrap(); - - assert_eq!(request, decoded); - } - - #[test] - fn encode_decode_find_node_request() { - let id = RequestId(vec![1]); - let request = Message::Request(Request { - id, - body: RequestBody::FindNode { - distances: vec![12], - }, - }); - - let encoded = request.clone().encode(); - let decoded = Message::decode(&encoded).unwrap(); - - assert_eq!(request, decoded); - } - - #[test] - fn encode_decode_nodes_response() { - let key = CombinedKey::generate_secp256k1(); - let enr1 = EnrBuilder::new("v4") - .ip4("127.0.0.1".parse().unwrap()) - .udp4(500) - .build(&key) - .unwrap(); - let enr2 = EnrBuilder::new("v4") - .ip4("10.0.0.1".parse().unwrap()) - .tcp4(8080) - .build(&key) - .unwrap(); - let enr3 = EnrBuilder::new("v4") - .ip("10.4.5.6".parse().unwrap()) - .build(&key) - .unwrap(); - - let enr_list = vec![enr1, enr2, enr3]; - let id = RequestId(vec![1]); - let request = Message::Response(Response { - id, - body: ResponseBody::Nodes { - total: 1, - nodes: enr_list, - }, - }); - - let encoded = request.clone().encode(); - let decoded = Message::decode(&encoded).unwrap(); - - assert_eq!(request, decoded); - } - - #[test] - fn encode_decode_ticket_request() { - let id = RequestId(vec![1]); - let request = Message::Request(Request { - id, - body: RequestBody::Talk { - protocol: vec![17u8; 32], - request: vec![1, 2, 3], - }, - }); - - let encoded = request.clone().encode(); - let decoded = Message::decode(&encoded).unwrap(); - - assert_eq!(request, decoded); - } - - /* - * These RPC messages are not in use yet - * - #[test] - fn ref_test_encode_request_ticket() { - // reference input - let id = 1; - let hash_bytes = - hex::decode("fb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736") - .unwrap(); - - // expected hex output - let expected_output = - hex::decode("05e201a0fb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736") - .unwrap(); - - let mut topic_hash = [0; 32]; - topic_hash.copy_from_slice(&hash_bytes); - - let message = Message::Request(Request { - id, - body: RequestBody::Ticket { topic: topic_hash }, - }); - assert_eq!(message.encode(), expected_output); - } - - #[test] - fn ref_test_encode_request_register_topic() { - // reference input - let id = 1; - let ticket = - hex::decode("fb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736") - .unwrap(); - - // expected hex output - let expected_output = - hex::decode("07e201a0fb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736") - .unwrap(); - - let message = Message::Request(Request { - id, - body: RequestBody::RegisterTopic { ticket }, - }); - assert_eq!(message.encode(), expected_output); - } - - #[test] - fn ref_test_encode_request_topic_query() { - // reference input - let id = 1; - let hash_bytes = - hex::decode("fb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736") - .unwrap(); - - // expected hex output - let expected_output = - hex::decode("09e201a0fb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736") - .unwrap(); - - let mut topic_hash = [0; 32]; - topic_hash.copy_from_slice(&hash_bytes); - - let message = Message::Request(Request { - id, - body: RequestBody::TopicQuery { topic: topic_hash }, - }); - assert_eq!(message.encode(), expected_output); - } - - #[test] - fn ref_test_encode_response_register_topic() { - // reference input - let id = 1; - let registered = true; - - // expected hex output - let expected_output = hex::decode("08c20101").unwrap(); - let message = Message::Response(Response { - id, - body: ResponseBody::RegisterTopic { registered }, - }); - assert_eq!(message.encode(), expected_output); - } - - #[test] - fn encode_decode_register_topic_request() { - let request = Message::Request(Request { - id: 1, - body: RequestBody::RegisterTopic { - topic: vec![1,2,3], - ticket: vec![1, 2, 3, 4, 5], - }, - }); - - let encoded = request.clone().encode(); - let decoded = Message::decode(encoded).unwrap(); - - assert_eq!(request, decoded); - } - - #[test] - fn encode_decode_register_topic_response() { - let request = Message::Response(Response { - id: 0, - body: ResponseBody::RegisterTopic { registered: true }, - }); - - let encoded = request.clone().encode(); - let decoded = Message::decode(encoded).unwrap(); - - assert_eq!(request, decoded); - } - - #[test] - fn encode_decode_topic_query_request() { - let request = Message::Request(Request { - id: 1, - body: RequestBody::TopicQuery { topic: [17u8; 32] }, - }); - - let encoded = request.clone().encode(); - let decoded = Message::decode(encoded).unwrap(); - - assert_eq!(request, decoded); - } - - #[test] - fn ref_test_encode_response_ticket() { - // reference input - let id = 1; - let ticket = [0; 32].to_vec(); // all 0's - let wait_time = 5; - - // expected hex output - let expected_output = hex::decode( - "06e301a0000000000000000000000000000000000000000000000000000000000000000005", - ) - .unwrap(); - - let message = Message::Response(Response { - id, - body: ResponseBody::Ticket { ticket, wait_time }, - }); - assert_eq!(message.encode(), expected_output); - } - - #[test] - fn encode_decode_ticket_response() { - let request = Message::Response(Response { - id: 0, - body: ResponseBody::Ticket { - ticket: vec![1, 2, 3, 4, 5], - wait_time: 5, - }, - }); - - let encoded = request.clone().encode(); - let decoded = Message::decode(encoded).unwrap(); - - assert_eq!(request, decoded); - } - - */ -} diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs new file mode 100644 index 000000000..d45e95310 --- /dev/null +++ b/src/rpc/mod.rs @@ -0,0 +1,468 @@ +use enr::{CombinedKey, Enr}; +use parse_display_derive::Display; +use rlp::DecoderError; +use std::net::{IpAddr, Ipv6Addr}; +use tracing::{debug, warn}; + +mod notification; +mod request; +mod response; + +pub use notification::Notification; +pub use request::{Request, RequestBody, RequestId}; +pub use response::{Response, ResponseBody}; + +/// Ping notification type. +pub const PING_MSG_TYPE: u8 = 1; +/// Pong notification type. +pub const PONG_MSG_TYPE: u8 = 2; +/// FindNode notification type. +pub const FINDNODE_MSG_TYPE: u8 = 3; +/// Nodes notification type. +pub const NODES_MSG_TYPE: u8 = 4; +/// TalkReq notification type. +pub const TALKREQ_MSG_TYPE: u8 = 5; +/// TalkResp notification type. +pub const TALKRESP_MSG_TYPE: u8 = 6; +/// RelayInit notification type. +pub const REALYINIT_MSG_TYPE: u8 = 7; +/// RelayMsg notification type. +pub const REALYMSG_MSG_TYPE: u8 = 8; + +#[derive(Debug, Clone, PartialEq, Eq, Display)] +/// A combined type representing requests and responses. +pub enum Message { + /// A request, which contains its [`RequestId`]. + #[display("{0}")] + Request(Request), + /// A Response, which contains the [`RequestId`] of its associated request. + #[display("{0}")] + Response(Response), + /// A unicast notification. + #[display("{0}")] + Notification(Notification), +} + +#[allow(dead_code)] +impl Message { + pub fn encode(self) -> Vec { + match self { + Self::Request(request) => request.encode(), + Self::Response(response) => response.encode(), + Self::Notification(notif) => notif.encode(), + } + } + + pub fn decode(data: &[u8]) -> Result { + if data.len() < 3 { + return Err(DecoderError::RlpIsTooShort); + } + + let msg_type = data[0]; + + let rlp = rlp::Rlp::new(&data[1..]); + + let list_len = rlp.item_count().and_then(|size| { + if size < 2 { + Err(DecoderError::RlpIncorrectListLen) + } else { + Ok(size) + } + })?; + + let id = RequestId::decode(rlp.val_at::>(0)?)?; + + let message = match msg_type { + 1 => { + // PingRequest + if list_len != 2 { + debug!( + "Ping Request has an invalid RLP list length. Expected 2, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + Message::Request(Request { + id, + body: RequestBody::Ping { + enr_seq: rlp.val_at::(1)?, + }, + }) + } + 2 => { + // PingResponse + if list_len != 4 { + debug!( + "Ping Response has an invalid RLP list length. Expected 4, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + let ip_bytes = rlp.val_at::>(2)?; + let ip = match ip_bytes.len() { + 4 => { + let mut ip = [0u8; 4]; + ip.copy_from_slice(&ip_bytes); + IpAddr::from(ip) + } + 16 => { + let mut ip = [0u8; 16]; + ip.copy_from_slice(&ip_bytes); + let ipv6 = Ipv6Addr::from(ip); + // If the ipv6 is ipv4 compatible/mapped, simply return the ipv4. + if let Some(ipv4) = ipv6.to_ipv4() { + IpAddr::V4(ipv4) + } else { + IpAddr::V6(ipv6) + } + } + _ => { + debug!("Ping Response has incorrect byte length for IP"); + return Err(DecoderError::RlpIncorrectListLen); + } + }; + let port = rlp.val_at::(3)?; + Message::Response(Response { + id, + body: ResponseBody::Pong { + enr_seq: rlp.val_at::(1)?, + ip, + port, + }, + }) + } + 3 => { + // FindNodeRequest + if list_len != 2 { + debug!( + "FindNode Request has an invalid RLP list length. Expected 2, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + let distances = rlp.list_at::(1)?; + + for distance in distances.iter() { + if distance > &256u64 { + warn!( + "Rejected FindNode request asking for unknown distance {}, maximum 256", + distance + ); + return Err(DecoderError::Custom("FINDNODE request distance invalid")); + } + } + + Message::Request(Request { + id, + body: RequestBody::FindNode { distances }, + }) + } + 4 => { + // NodesResponse + if list_len != 3 { + debug!( + "Nodes Response has an invalid RLP list length. Expected 3, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + + let nodes = { + let enr_list_rlp = rlp.at(2)?; + if enr_list_rlp.is_empty() { + // no records + vec![] + } else { + enr_list_rlp.as_list::>()? + } + }; + Message::Response(Response { + id, + body: ResponseBody::Nodes { + total: rlp.val_at::(1)?, + nodes, + }, + }) + } + 5 => { + // Talk Request + if list_len != 3 { + debug!( + "Talk Request has an invalid RLP list length. Expected 3, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + let protocol = rlp.val_at::>(1)?; + let request = rlp.val_at::>(2)?; + Message::Request(Request { + id, + body: RequestBody::TalkReq { protocol, request }, + }) + } + 6 => { + // Talk Response + if list_len != 2 { + debug!( + "Talk Response has an invalid RLP list length. Expected 2, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + let response = rlp.val_at::>(1)?; + Message::Response(Response { + id, + body: ResponseBody::TalkResp { response }, + }) + } + _ => { + return Err(DecoderError::Custom("Unknown RPC message type")); + } + }; + + Ok(message) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use enr::EnrBuilder; + + #[test] + fn ref_test_encode_request_ping() { + // reference input + let id = RequestId(vec![1]); + let enr_seq = 1; + let message = Message::Request(Request { + id, + body: RequestBody::Ping { enr_seq }, + }); + + // expected hex output + let expected_output = hex::decode("01c20101").unwrap(); + + dbg!(hex::encode(message.clone().encode())); + assert_eq!(message.encode(), expected_output); + } + + #[test] + fn ref_test_encode_request_findnode() { + // reference input + let id = RequestId(vec![1]); + let distances = vec![256]; + let message = Message::Request(Request { + id, + body: RequestBody::FindNode { distances }, + }); + + // expected hex output + let expected_output = hex::decode("03c501c3820100").unwrap(); + dbg!(hex::encode(message.clone().encode())); + + assert_eq!(message.encode(), expected_output); + } + + #[test] + fn ref_test_encode_response_ping() { + // reference input + let id = RequestId(vec![1]); + let enr_seq = 1; + let ip: IpAddr = "127.0.0.1".parse().unwrap(); + let port = 5000; + let message = Message::Response(Response { + id, + body: ResponseBody::Pong { enr_seq, ip, port }, + }); + + // expected hex output + let expected_output = hex::decode("02ca0101847f000001821388").unwrap(); + + dbg!(hex::encode(message.clone().encode())); + assert_eq!(message.encode(), expected_output); + } + + #[test] + fn ref_test_encode_response_nodes_empty() { + // reference input + let id = RequestId(vec![1]); + let total = 1; + + // expected hex output + let expected_output = hex::decode("04c30101c0").unwrap(); + + let message = Message::Response(Response { + id, + body: ResponseBody::Nodes { + total, + nodes: vec![], + }, + }); + assert_eq!(message.encode(), expected_output); + } + + #[test] + fn ref_test_encode_response_nodes() { + // reference input + let id = RequestId(vec![1]); + let total = 1; + + let enr = "-HW4QCjfjuCfSmIJHxqLYfGKrSz-Pq3G81DVJwd_muvFYJiIOkf0bGtJu7kZVCOPnhSTMneyvR4MRbF3G5TNB4wy2ssBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg".parse::>().unwrap(); + // expected hex output + let expected_output = hex::decode("04f87b0101f877f875b84028df8ee09f4a62091f1a8b61f18aad2cfe3eadc6f350d527077f9aebc56098883a47f46c6b49bbb91954238f9e14933277b2bd1e0c45b1771b94cd078c32dacb0182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138").unwrap(); + + let message = Message::Response(Response { + id, + body: ResponseBody::Nodes { + total, + nodes: vec![enr], + }, + }); + dbg!(hex::encode(message.clone().encode())); + assert_eq!(message.encode(), expected_output); + } + + #[test] + fn ref_test_encode_response_nodes_multiple() { + // reference input + let id = RequestId(vec![1]); + let total = 1; + let enr = "enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg".parse::>().unwrap(); + + let enr2 = "enr:-HW4QNfxw543Ypf4HXKXdYxkyzfcxcO-6p9X986WldfVpnVTQX1xlTnWrktEWUbeTZnmgOuAY_KUhbVV1Ft98WoYUBMBgmlkgnY0iXNlY3AyNTZrMaEDDiy3QkHAxPyOgWbxp5oF1bDdlYE6dLCUUp8xfVw50jU".parse::>().unwrap(); + + // expected hex output + let expected_output = hex::decode("04f8f20101f8eef875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235").unwrap(); + + let message = Message::Response(Response { + id, + body: ResponseBody::Nodes { + total, + nodes: vec![enr, enr2], + }, + }); + dbg!(hex::encode(message.clone().encode())); + assert_eq!(message.encode(), expected_output); + } + + #[test] + fn ref_decode_response_nodes_multiple() { + let input = hex::decode("04f8f20101f8eef875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235").unwrap(); + + let expected_enr1 = "enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg".parse::>().unwrap(); + let expected_enr2 = "enr:-HW4QNfxw543Ypf4HXKXdYxkyzfcxcO-6p9X986WldfVpnVTQX1xlTnWrktEWUbeTZnmgOuAY_KUhbVV1Ft98WoYUBMBgmlkgnY0iXNlY3AyNTZrMaEDDiy3QkHAxPyOgWbxp5oF1bDdlYE6dLCUUp8xfVw50jU".parse::>().unwrap(); + + let decoded = Message::decode(&input).unwrap(); + + match decoded { + Message::Response(response) => match response.body { + ResponseBody::Nodes { total, nodes } => { + assert_eq!(total, 1); + assert_eq!(nodes[0], expected_enr1); + assert_eq!(nodes[1], expected_enr2); + } + _ => panic!("Invalid decoding"), + }, + _ => panic!("Invalid decoding"), + } + } + + #[test] + fn encode_decode_ping_request() { + let id = RequestId(vec![1]); + let request = Message::Request(Request { + id, + body: RequestBody::Ping { enr_seq: 15 }, + }); + + let encoded = request.clone().encode(); + let decoded = Message::decode(&encoded).unwrap(); + + assert_eq!(request, decoded); + } + + #[test] + fn encode_decode_ping_response() { + let id = RequestId(vec![1]); + let request = Message::Response(Response { + id, + body: ResponseBody::Pong { + enr_seq: 15, + ip: "127.0.0.1".parse().unwrap(), + port: 80, + }, + }); + + let encoded = request.clone().encode(); + let decoded = Message::decode(&encoded).unwrap(); + + assert_eq!(request, decoded); + } + + #[test] + fn encode_decode_find_node_request() { + let id = RequestId(vec![1]); + let request = Message::Request(Request { + id, + body: RequestBody::FindNode { + distances: vec![12], + }, + }); + + let encoded = request.clone().encode(); + let decoded = Message::decode(&encoded).unwrap(); + + assert_eq!(request, decoded); + } + + #[test] + fn encode_decode_nodes_response() { + let key = CombinedKey::generate_secp256k1(); + let enr1 = EnrBuilder::new("v4") + .ip4("127.0.0.1".parse().unwrap()) + .udp4(500) + .build(&key) + .unwrap(); + let enr2 = EnrBuilder::new("v4") + .ip4("10.0.0.1".parse().unwrap()) + .tcp4(8080) + .build(&key) + .unwrap(); + let enr3 = EnrBuilder::new("v4") + .ip("10.4.5.6".parse().unwrap()) + .build(&key) + .unwrap(); + + let enr_list = vec![enr1, enr2, enr3]; + let id = RequestId(vec![1]); + let request = Message::Response(Response { + id, + body: ResponseBody::Nodes { + total: 1, + nodes: enr_list, + }, + }); + + let encoded = request.clone().encode(); + let decoded = Message::decode(&encoded).unwrap(); + + assert_eq!(request, decoded); + } + + #[test] + fn encode_decode_talk_request() { + let id = RequestId(vec![1]); + let request = Message::Request(Request { + id, + body: RequestBody::TalkReq { + protocol: vec![17u8; 32], + request: vec![1, 2, 3], + }, + }); + + let encoded = request.clone().encode(); + let decoded = Message::decode(&encoded).unwrap(); + + assert_eq!(request, decoded); + } +} diff --git a/src/rpc/notification/mod.rs b/src/rpc/notification/mod.rs new file mode 100644 index 000000000..37158b605 --- /dev/null +++ b/src/rpc/notification/mod.rs @@ -0,0 +1,63 @@ +use super::{REALYINIT_MSG_TYPE, REALYMSG_MSG_TYPE}; +use crate::{impl_from_variant_wrap, packet::MessageNonce}; +use parse_display_derive::Display; +use rlp::{Decodable, DecoderError, Rlp, RlpStream}; + +mod relay_init; +mod relay_msg; + +pub use relay_init::RelayInit; +pub use relay_msg::RelayMsg; + +/// Nonce of request that triggered the initiation of this hole punching attempt. +type NonceOfTimedOutMessage = MessageNonce; + +/// A unicast notification sent over discv5. +#[derive(Debug, Display, PartialEq, Eq, Clone)] +pub enum Notification { + /// A notification to initialise a one-shot relay circuit for hole-punching. + #[display("Notification: {0}")] + RelayInit(pub Enr, pub NodeId, pub NonceOfTimedOutMessage), + /// The notification relayed to target of hole punch attempt. + #[display("Notification: {0}")] + RelayMsg(pub Enr, pub NonceOfTimedOutMessage), +} + +impl_from_variant_wrap!(, RelayInit, Notification, Self::RelayInit); +impl_from_variant_wrap!(, RelayMsg, Notification, Self::RelayMsg); + +impl Notification { + pub fn msg_type(&self) -> u8 { + match self { + Self::RelayInit(..) => 7, + Self::RelayMsg(..) => 8, + } + } + + /// Encodes a notification message to RLP-encoded bytes. + pub fn encode(self) -> Vec { + let mut buf = Vec::with_capacity(100); + let msg_type = self.msg_type(); + buf.push(msg_type); + let mut s = RlpStream::new(); + let _ = match self { + Self::RelayInit(notif) => s.append(¬if), + Self::RelayMsg(notif) => s.append(¬if), + }; + buf.extend_from_slice(&s.out()); + buf + } + + /// Decodes RLP-encoded bytes into a notification message. + pub fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { + match msg_type { + REALYINIT_MSG_TYPE => Ok(RelayInit::decode(rlp)?.into()), + REALYMSG_MSG_TYPE => Ok(RelayMsg::decode(rlp)?.into()), + _ => { + return Err(DecoderError::Custom( + "Unknown RPC notification message type", + )) + } + } + } +} diff --git a/src/rpc/notification/relay_init.rs b/src/rpc/notification/relay_init.rs new file mode 100644 index 000000000..1aed6d35b --- /dev/null +++ b/src/rpc/notification/relay_init.rs @@ -0,0 +1,75 @@ +use super::Notification; +use crate::{ + impl_from_variant_unwrap, + packet::{MessageNonce, MESSAGE_NONCE_LENGTH}, + Enr, +}; +use enr::NodeId; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; +use std::fmt; + +/// Node id length in bytes. +const NODE_ID_LENGTH: usize = 32; +/// Nonce of request that triggered the initiation of this hole punching attempt. +type NonceOfTimedOutMessage = MessageNonce; + +/// A notification sent from the initiator to the relay. Contains the enr of the initiator, the +/// nonce of the timed out request and the node id of the target. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct RelayInit(pub Enr, pub NodeId, pub NonceOfTimedOutMessage); + +impl_from_variant_unwrap!(, Notification, RelayInit, Notification::RelayInit); + +impl Encodable for RelayInit { + fn rlp_append(&self, s: &mut RlpStream) { + let RelayInit(initiator, target, nonce) = self; + + s.begin_list(3); + s.append(initiator); + s.append(&(&target.raw() as &[u8])); + s.append(&(nonce as &[u8])); + } +} + +impl Decodable for RelayInit { + fn decode(rlp: &Rlp<'_>) -> Result { + if rlp.item_count()? != 3 { + return Err(DecoderError::RlpIncorrectListLen); + } + let initiator = rlp.val_at::(0)?; + + let tgt_bytes = rlp.val_at::>(1)?; + if tgt_bytes.len() > NODE_ID_LENGTH { + return Err(DecoderError::RlpIsTooBig); + } + let mut tgt = [0u8; NODE_ID_LENGTH]; + tgt[NODE_ID_LENGTH - tgt_bytes.len()..].copy_from_slice(&tgt_bytes); + let tgt = NodeId::from(tgt); + + let nonce_bytes = rlp.val_at::>(2)?; + if nonce_bytes.len() > MESSAGE_NONCE_LENGTH { + return Err(DecoderError::RlpIsTooBig); + } + let mut nonce = [0u8; MESSAGE_NONCE_LENGTH]; + nonce[MESSAGE_NONCE_LENGTH - nonce_bytes.len()..].copy_from_slice(&nonce_bytes); + + Ok(RelayInit(initiator, tgt, nonce)) + } +} + +impl fmt::Display for RelayInit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let initiator = &self.0; + let tgt = hex::encode(self.1); + let nonce = hex::encode(self.2); + write!( + f, + "RelayInit: Initiator: {}, Target: 0x{}..{}, Nonce: 0x{}..{}", + initiator, + &tgt[0..4], + &tgt[tgt.len() - 4..], + &nonce[0..2], + &nonce[nonce.len() - 2..] + ) + } +} diff --git a/src/rpc/notification/relay_msg.rs b/src/rpc/notification/relay_msg.rs new file mode 100644 index 000000000..0c7bff82d --- /dev/null +++ b/src/rpc/notification/relay_msg.rs @@ -0,0 +1,60 @@ +use super::Notification; +use crate::{ + impl_from_variant_unwrap, + packet::{MessageNonce, MESSAGE_NONCE_LENGTH}, + Enr, +}; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; +use std::fmt; + +// Nonce of request that triggered the initiation of this hole punching attempt. +type NonceOfTimedOutMessage = MessageNonce; + +/// A notification sent from the initiator to the relay. Contains the enr of the initiator and the +/// nonce of the timed out request. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct RelayMsg(pub Enr, pub NonceOfTimedOutMessage); + +impl_from_variant_unwrap!(, Notification, RelayMsg, Notification::RelayMsg); + +impl Encodable for RelayMsg { + fn rlp_append(&self, s: &mut RlpStream) { + let RelayMsg(initiator, nonce) = self; + + s.begin_list(2); + s.append(initiator); + s.append(&(nonce as &[u8])); + } +} + +impl Decodable for RelayMsg { + fn decode(rlp: &Rlp<'_>) -> Result { + if rlp.item_count()? != 2 { + return Err(DecoderError::RlpIncorrectListLen); + } + let initiator = rlp.val_at::(0)?; + + let nonce_bytes = rlp.val_at::>(2)?; + if nonce_bytes.len() > MESSAGE_NONCE_LENGTH { + return Err(DecoderError::RlpIsTooBig); + } + let mut nonce = [0u8; MESSAGE_NONCE_LENGTH]; + nonce[MESSAGE_NONCE_LENGTH - nonce_bytes.len()..].copy_from_slice(&nonce_bytes); + + Ok(RelayMsg(initiator, nonce).into()) + } +} + +impl fmt::Display for RelayMsg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let initiator = &self.0; + let nonce = hex::encode(self.1); + write!( + f, + "RelayMsg: Initiator: {}, Nonce: 0x{}..{}", + initiator, + &nonce[0..2], + &nonce[nonce.len() - 2..] + ) + } +} diff --git a/src/rpc/request/mod.rs b/src/rpc/request/mod.rs new file mode 100644 index 000000000..ed2932e13 --- /dev/null +++ b/src/rpc/request/mod.rs @@ -0,0 +1,166 @@ +use super::{FINDNODE_MSG_TYPE, PING_MSG_TYPE, TALKREQ_MSG_TYPE}; +use crate::impl_from_tuple_struct_unwrap; +use parse_display_derive::Display; +use rlp::{DecoderError, Rlp, RlpStream}; +use tracing::{debug, warn}; + +mod request_body; + +pub use request_body::RequestBody; + +/// A request sent between nodes. +#[derive(Debug, Clone, PartialEq, Eq, Display)] +#[display("Request: id: {id}: {body}")] +pub struct Request { + /// The [`RequestId`] of the request. + pub id: RequestId, + /// The body of the request. + pub body: RequestBody, +} + +impl Request { + pub fn msg_type(&self) -> u8 { + match self.body { + RequestBody::Ping { .. } => 1, + RequestBody::FindNode { .. } => 3, + RequestBody::TalkReq { .. } => 5, + } + } + + /// Encodes a request message to RLP-encoded bytes. + pub fn encode(self) -> Vec { + let mut buf = Vec::with_capacity(10); + let msg_type = self.msg_type(); + buf.push(msg_type); + let id = &self.id; + match self.body { + RequestBody::Ping { enr_seq } => { + let mut s = RlpStream::new(); + s.begin_list(2); + s.append(&id.as_bytes()); + s.append(&enr_seq); + buf.extend_from_slice(&s.out()); + buf + } + RequestBody::FindNode { distances } => { + let mut s = RlpStream::new(); + s.begin_list(2); + s.append(&id.as_bytes()); + s.begin_list(distances.len()); + for distance in distances { + s.append(&distance); + } + buf.extend_from_slice(&s.out()); + buf + } + RequestBody::TalkReq { protocol, request } => { + let mut s = RlpStream::new(); + s.begin_list(3); + s.append(&id.as_bytes()); + s.append(&protocol); + s.append(&request); + buf.extend_from_slice(&s.out()); + buf + } + } + } + + /// Decodes RLP-encoded bytes into a request message. + pub fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { + let list_len = rlp.item_count()?; + let id = RequestId::decode(rlp.val_at::>(0)?)?; + let message = match msg_type { + PING_MSG_TYPE => { + // Ping Request + if list_len != 2 { + debug!( + "Ping Request has an invalid RLP list length. Expected 2, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + Self { + id, + body: RequestBody::Ping { + enr_seq: rlp.val_at::(1)?, + }, + } + } + FINDNODE_MSG_TYPE => { + // FindNode Request + if list_len != 2 { + debug!( + "FindNode Request has an invalid RLP list length. Expected 2, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + let distances = rlp.list_at::(1)?; + + for distance in distances.iter() { + if distance > &256u64 { + warn!( + "Rejected FindNode request asking for unknown distance {}, maximum 256", + distance + ); + return Err(DecoderError::Custom("FINDNODE request distance invalid")); + } + } + + Self { + id, + body: RequestBody::FindNode { distances }, + } + } + TALKREQ_MSG_TYPE => { + // Talk Request + if list_len != 3 { + debug!( + "Talk Request has an invalid RLP list length. Expected 3, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + let protocol = rlp.val_at::>(1)?; + let request = rlp.val_at::>(2)?; + Self { + id, + body: RequestBody::TalkReq { protocol, request }, + } + } + _ => return Err(DecoderError::Custom("Unknown RPC request message type")), + }; + Ok(message) + } +} + +/// Type to manage the request IDs. +#[derive(Debug, Clone, PartialEq, Hash, Eq)] +pub struct RequestId(pub Vec); + +impl_from_tuple_struct_unwrap!(, RequestId, Vec); + +impl RequestId { + /// Decodes the ID from a raw bytes. + pub fn decode(data: Vec) -> Result { + if data.len() > 8 { + return Err(DecoderError::Custom("Invalid ID length")); + } + Ok(RequestId(data)) + } + + pub fn random() -> Self { + let rand: u64 = rand::random(); + RequestId(rand.to_be_bytes().to_vec()) + } + + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +impl std::fmt::Display for RequestId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(&self.0)) + } +} diff --git a/src/rpc/request/request_body.rs b/src/rpc/request/request_body.rs new file mode 100644 index 000000000..08ae058de --- /dev/null +++ b/src/rpc/request/request_body.rs @@ -0,0 +1,37 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RequestBody { + /// A PING request. + Ping { + /// Our current ENR sequence number. + enr_seq: u64, + }, + /// A FINDNODE request. + FindNode { + /// The distance(s) of peers we expect to be returned in the response. + distances: Vec, + }, + /// A Talk request. + TalkReq { + /// The protocol requesting. + protocol: Vec, + /// The request. + request: Vec, + }, +} + +impl std::fmt::Display for RequestBody { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + RequestBody::Ping { enr_seq } => write!(f, "PING: enr_seq: {enr_seq}"), + RequestBody::FindNode { distances } => { + write!(f, "FINDNODE Request: distance: {distances:?}") + } + RequestBody::TalkReq { protocol, request } => write!( + f, + "TALK: protocol: {}, request: {}", + hex::encode(protocol), + hex::encode(request) + ), + } + } +} diff --git a/src/rpc/response/mod.rs b/src/rpc/response/mod.rs new file mode 100644 index 000000000..63bec2fef --- /dev/null +++ b/src/rpc/response/mod.rs @@ -0,0 +1,183 @@ +use super::{RequestBody, RequestId, NODES_MSG_TYPE, PONG_MSG_TYPE, TALKRESP_MSG_TYPE}; +use crate::Enr; +use parse_display_derive::Display; +use rlp::{DecoderError, Rlp, RlpStream}; +use std::net::{IpAddr, Ipv6Addr}; +use tracing::debug; + +mod response_body; + +pub use response_body::ResponseBody; + +/// A response sent in response to a [`super::Request`] +#[derive(Debug, Clone, PartialEq, Eq, Display)] +#[display("Response: id: {id}: {body}")] +pub struct Response { + /// The [`RequestId`] of the request that triggered this response. + pub id: RequestId, + /// The body of this response. + pub body: ResponseBody, +} + +impl Response { + pub fn msg_type(&self) -> u8 { + match &self.body { + ResponseBody::Pong { .. } => 2, + ResponseBody::Nodes { .. } => 4, + ResponseBody::TalkResp { .. } => 6, + } + } + + /// Determines if the response is a valid response to the given request. + pub fn match_request(&self, req: &RequestBody) -> bool { + match self.body { + ResponseBody::Pong { .. } => matches!(req, RequestBody::Ping { .. }), + ResponseBody::Nodes { .. } => { + matches!(req, RequestBody::FindNode { .. }) + } + ResponseBody::TalkResp { .. } => matches!(req, RequestBody::TalkReq { .. }), + } + } + + /// Encodes a response message to RLP-encoded bytes. + pub fn encode(self) -> Vec { + let mut buf = Vec::with_capacity(10); + let msg_type = self.msg_type(); + buf.push(msg_type); + let id = &self.id; + match self.body { + ResponseBody::Pong { enr_seq, ip, port } => { + let mut s = RlpStream::new(); + s.begin_list(4); + s.append(&id.as_bytes()); + s.append(&enr_seq); + match ip { + IpAddr::V4(addr) => s.append(&(&addr.octets() as &[u8])), + IpAddr::V6(addr) => s.append(&(&addr.octets() as &[u8])), + }; + s.append(&port); + buf.extend_from_slice(&s.out()); + buf + } + ResponseBody::Nodes { total, nodes } => { + let mut s = RlpStream::new(); + s.begin_list(3); + s.append(&id.as_bytes()); + s.append(&total); + + if nodes.is_empty() { + s.begin_list(0); + } else { + s.begin_list(nodes.len()); + for node in nodes { + s.append(&node); + } + } + buf.extend_from_slice(&s.out()); + buf + } + ResponseBody::TalkResp { response } => { + let mut s = RlpStream::new(); + s.begin_list(2); + s.append(&id.as_bytes()); + s.append(&response); + buf.extend_from_slice(&s.out()); + buf + } + } + } + + /// Decodes RLP-encoded bytes into a response message. + pub fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { + let list_len = rlp.item_count()?; + let id = RequestId::decode(rlp.val_at::>(0)?)?; + let response = match msg_type { + PONG_MSG_TYPE => { + // Pong Response + if list_len != 4 { + debug!( + "Ping Response has an invalid RLP list length. Expected 4, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + let ip_bytes = rlp.val_at::>(2)?; + let ip = match ip_bytes.len() { + 4 => { + let mut ip = [0u8; 4]; + ip.copy_from_slice(&ip_bytes); + IpAddr::from(ip) + } + 16 => { + let mut ip = [0u8; 16]; + ip.copy_from_slice(&ip_bytes); + let ipv6 = Ipv6Addr::from(ip); + // If the ipv6 is ipv4 compatible/mapped, simply return the ipv4. + if let Some(ipv4) = ipv6.to_ipv4() { + IpAddr::V4(ipv4) + } else { + IpAddr::V6(ipv6) + } + } + _ => { + debug!("Ping Response has incorrect byte length for IP"); + return Err(DecoderError::RlpIncorrectListLen); + } + }; + let port = rlp.val_at::(3)?; + Self { + id, + body: ResponseBody::Pong { + enr_seq: rlp.val_at::(1)?, + ip, + port, + }, + } + } + NODES_MSG_TYPE => { + // Nodes Response + if list_len != 3 { + debug!( + "Nodes Response has an invalid RLP list length. Expected 3, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + + let nodes = { + let enr_list_rlp = rlp.at(2)?; + if enr_list_rlp.is_empty() { + // no records + vec![] + } else { + enr_list_rlp.as_list::()? + } + }; + Self { + id, + body: ResponseBody::Nodes { + total: rlp.val_at::(1)?, + nodes, + }, + } + } + TALKRESP_MSG_TYPE => { + // Talk Response + if list_len != 2 { + debug!( + "Talk Response has an invalid RLP list length. Expected 2, found {}", + list_len + ); + return Err(DecoderError::RlpIncorrectListLen); + } + let response = rlp.val_at::>(1)?; + Self { + id, + body: ResponseBody::TalkResp { response }, + } + } + _ => return Err(DecoderError::Custom("Unknown RPC response message type")), + }; + Ok(response) + } +} diff --git a/src/rpc/response/response_body.rs b/src/rpc/response/response_body.rs new file mode 100644 index 000000000..e3dab037a --- /dev/null +++ b/src/rpc/response/response_body.rs @@ -0,0 +1,54 @@ +use crate::Enr; +use std::net::IpAddr; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ResponseBody { + /// A PONG response. + Pong { + /// The current ENR sequence number of the responder. + enr_seq: u64, + /// Our external IP address as observed by the responder. + ip: IpAddr, + /// Our external UDP port as observed by the responder. + port: u16, + }, + /// A NODES response. + Nodes { + /// The total number of responses that make up this response. + total: u64, + /// A list of ENR's returned by the responder. + nodes: Vec, + }, + /// The TALK response. + TalkResp { + /// The response for the talk. + response: Vec, + }, +} + +impl std::fmt::Display for ResponseBody { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ResponseBody::Pong { enr_seq, ip, port } => { + write!(f, "PONG: Enr-seq: {enr_seq}, Ip: {ip:?}, Port: {port}") + } + ResponseBody::Nodes { total, nodes } => { + write!(f, "NODES: total: {total}, Nodes: [")?; + let mut first = true; + for id in nodes { + if !first { + write!(f, ", {id}")?; + } else { + write!(f, "{id}")?; + } + first = false; + } + + write!(f, "]") + } + ResponseBody::TalkResp { response } => { + write!(f, "Response: Response {}", hex::encode(response)) + } + } + } +} diff --git a/src/service.rs b/src/service.rs index aec54c95d..d5d8120b6 100644 --- a/src/service.rs +++ b/src/service.rs @@ -79,7 +79,7 @@ impl Drop for TalkRequest { let response = Response { id: self.id.clone(), - body: ResponseBody::Talk { response: vec![] }, + body: ResponseBody::TalkResp { response: vec![] }, }; debug!("Sending empty TALK response to {}", self.node_address); @@ -114,7 +114,7 @@ impl TalkRequest { let response = Response { id: self.id.clone(), - body: ResponseBody::Talk { response }, + body: ResponseBody::TalkResp { response }, }; self.sender @@ -605,7 +605,7 @@ impl Service { warn!("Failed to send response {}", e) } } - RequestBody::Talk { protocol, request } => { + RequestBody::TalkReq { protocol, request } => { let req = TalkRequest { id, node_address, @@ -616,12 +616,6 @@ impl Service { self.send_event(Discv5Event::TalkRequest(req)); } - RequestBody::RegisterTopic { .. } => { - debug!("Received RegisterTopic request which is unimplemented"); - } - RequestBody::TopicQuery { .. } => { - debug!("Received TopicQuery request which is unimplemented"); - } } } @@ -897,7 +891,7 @@ impl Service { self.connection_updated(node_id, ConnectionStatus::PongReceived(enr)); } } - ResponseBody::Talk { response } => { + ResponseBody::TalkResp { response } => { // Send the response to the user match active_request.callback { Some(CallbackResponse::Talk(callback)) => { @@ -908,12 +902,6 @@ impl Service { _ => error!("Invalid callback for response"), } } - ResponseBody::Ticket { .. } => { - error!("Received a TICKET response. This is unimplemented and should be unreachable."); - } - ResponseBody::RegisterConfirmation { .. } => { - error!("Received a RegisterConfirmation response. This is unimplemented and should be unreachable."); - } } } else { warn!( @@ -990,7 +978,7 @@ impl Service { request: Vec, callback: oneshot::Sender, RequestError>>, ) { - let request_body = RequestBody::Talk { protocol, request }; + let request_body = RequestBody::TalkReq { protocol, request }; let active_request = ActiveRequest { contact, From afa637bb4abee5917d8df4c01f3262bf1347a5b7 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 30 Apr 2023 13:07:05 +0200 Subject: [PATCH 044/154] notif sep --- src/handler/mod.rs | 10 +++--- src/handler/sessions/crypto/mod.rs | 2 +- src/rpc/mod.rs | 58 ++++++++++++++++-------------- src/rpc/notification/mod.rs | 23 ++++++------ src/rpc/request/mod.rs | 16 ++++----- src/rpc/response/mod.rs | 6 ++-- 6 files changed, 61 insertions(+), 54 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index f0d191bb9..8088a463a 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -31,7 +31,7 @@ use crate::{ discv5::PERMIT_BAN_LIST, error::{Discv5Error, RequestError}, packet::{ChallengeData, IdNonce, MessageNonce, Packet, PacketKind, ProtocolIdentity}, - rpc::{Message, Request, RequestBody, RequestId, Response, ResponseBody}, + rpc::{Payload, Request, RequestBody, RequestId, Response, ResponseBody}, socket, socket::{FilterConfig, Outbound, Socket}, Enr, @@ -991,7 +991,7 @@ impl Handler

{ ); return; } - Ok(ref bytes) => match Message::decode(bytes) { + Ok(ref bytes) => match Payload::decode(bytes) { Ok(message) => message, Err(msg_err) => { // try to decode the message as an application layer notification @@ -1008,7 +1008,7 @@ impl Handler

{ }; match message { - Message::Response(response) => { + Payload::Response(response) => { // Sessions could be awaiting an ENR response. Check if this response matches // these let Some(request_id) = session.awaiting_enr.as_ref() else { @@ -1083,7 +1083,7 @@ impl Handler

{ }; // attempt to decrypt and process the message. let message = match session.decrypt_message(message_nonce, message, authenticated_data) { - Ok(m) => match Message::decode(&m) { + Ok(m) => match Payload::decode(&m) { Ok(p) => p, Err(e) => { warn!("Failed to decode message. Error: {:?}, {}", e, node_address); @@ -1123,7 +1123,7 @@ impl Handler

{ trace!("Received message from: {}", node_address); match message { - Message::Request(request) => { + Payload::Request(request) => { // report the request to the application if let Err(e) = self .service_send diff --git a/src/handler/sessions/crypto/mod.rs b/src/handler/sessions/crypto/mod.rs index 3ddabc984..b80c20d85 100644 --- a/src/handler/sessions/crypto/mod.rs +++ b/src/handler/sessions/crypto/mod.rs @@ -410,7 +410,7 @@ mod tests { let message = decrypt_message(&key, nonce, &ciphertext, &auth_data).unwrap(); dbg!(&message); dbg!(hex::encode(&message)); - let rpc = crate::rpc::Message::decode(&message).unwrap(); + let rpc = crate::rpc::Payload::decode(&message).unwrap(); println!("{rpc}"); } diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index d45e95310..4030d256e 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -1,6 +1,6 @@ use enr::{CombinedKey, Enr}; use parse_display_derive::Display; -use rlp::DecoderError; +use rlp::{DecoderError, Rlp}; use std::net::{IpAddr, Ipv6Addr}; use tracing::{debug, warn}; @@ -29,8 +29,14 @@ pub const REALYINIT_MSG_TYPE: u8 = 7; /// RelayMsg notification type. pub const REALYMSG_MSG_TYPE: u8 = 8; +pub trait Payload where Self: Sized { + fn msg_type(&self) -> u8; + fn encode(self) -> Vec; + fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result; +} + #[derive(Debug, Clone, PartialEq, Eq, Display)] -/// A combined type representing requests and responses. +/// A combined type representing the packet payloads. pub enum Message { /// A request, which contains its [`RequestId`]. #[display("{0}")] @@ -82,7 +88,7 @@ impl Message { ); return Err(DecoderError::RlpIncorrectListLen); } - Message::Request(Request { + Payload::Request(Request { id, body: RequestBody::Ping { enr_seq: rlp.val_at::(1)?, @@ -122,7 +128,7 @@ impl Message { } }; let port = rlp.val_at::(3)?; - Message::Response(Response { + Payload::Response(Response { id, body: ResponseBody::Pong { enr_seq: rlp.val_at::(1)?, @@ -152,7 +158,7 @@ impl Message { } } - Message::Request(Request { + Payload::Request(Request { id, body: RequestBody::FindNode { distances }, }) @@ -176,7 +182,7 @@ impl Message { enr_list_rlp.as_list::>()? } }; - Message::Response(Response { + Payload::Response(Response { id, body: ResponseBody::Nodes { total: rlp.val_at::(1)?, @@ -195,7 +201,7 @@ impl Message { } let protocol = rlp.val_at::>(1)?; let request = rlp.val_at::>(2)?; - Message::Request(Request { + Payload::Request(Request { id, body: RequestBody::TalkReq { protocol, request }, }) @@ -210,7 +216,7 @@ impl Message { return Err(DecoderError::RlpIncorrectListLen); } let response = rlp.val_at::>(1)?; - Message::Response(Response { + Payload::Response(Response { id, body: ResponseBody::TalkResp { response }, }) @@ -234,7 +240,7 @@ mod tests { // reference input let id = RequestId(vec![1]); let enr_seq = 1; - let message = Message::Request(Request { + let message = Payload::Request(Request { id, body: RequestBody::Ping { enr_seq }, }); @@ -251,7 +257,7 @@ mod tests { // reference input let id = RequestId(vec![1]); let distances = vec![256]; - let message = Message::Request(Request { + let message = Payload::Request(Request { id, body: RequestBody::FindNode { distances }, }); @@ -270,7 +276,7 @@ mod tests { let enr_seq = 1; let ip: IpAddr = "127.0.0.1".parse().unwrap(); let port = 5000; - let message = Message::Response(Response { + let message = Payload::Response(Response { id, body: ResponseBody::Pong { enr_seq, ip, port }, }); @@ -291,7 +297,7 @@ mod tests { // expected hex output let expected_output = hex::decode("04c30101c0").unwrap(); - let message = Message::Response(Response { + let message = Payload::Response(Response { id, body: ResponseBody::Nodes { total, @@ -311,7 +317,7 @@ mod tests { // expected hex output let expected_output = hex::decode("04f87b0101f877f875b84028df8ee09f4a62091f1a8b61f18aad2cfe3eadc6f350d527077f9aebc56098883a47f46c6b49bbb91954238f9e14933277b2bd1e0c45b1771b94cd078c32dacb0182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138").unwrap(); - let message = Message::Response(Response { + let message = Payload::Response(Response { id, body: ResponseBody::Nodes { total, @@ -334,7 +340,7 @@ mod tests { // expected hex output let expected_output = hex::decode("04f8f20101f8eef875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235").unwrap(); - let message = Message::Response(Response { + let message = Payload::Response(Response { id, body: ResponseBody::Nodes { total, @@ -352,10 +358,10 @@ mod tests { let expected_enr1 = "enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg".parse::>().unwrap(); let expected_enr2 = "enr:-HW4QNfxw543Ypf4HXKXdYxkyzfcxcO-6p9X986WldfVpnVTQX1xlTnWrktEWUbeTZnmgOuAY_KUhbVV1Ft98WoYUBMBgmlkgnY0iXNlY3AyNTZrMaEDDiy3QkHAxPyOgWbxp5oF1bDdlYE6dLCUUp8xfVw50jU".parse::>().unwrap(); - let decoded = Message::decode(&input).unwrap(); + let decoded = Payload::decode(&input).unwrap(); match decoded { - Message::Response(response) => match response.body { + Payload::Response(response) => match response.body { ResponseBody::Nodes { total, nodes } => { assert_eq!(total, 1); assert_eq!(nodes[0], expected_enr1); @@ -370,13 +376,13 @@ mod tests { #[test] fn encode_decode_ping_request() { let id = RequestId(vec![1]); - let request = Message::Request(Request { + let request = Payload::Request(Request { id, body: RequestBody::Ping { enr_seq: 15 }, }); let encoded = request.clone().encode(); - let decoded = Message::decode(&encoded).unwrap(); + let decoded = Payload::decode(&encoded).unwrap(); assert_eq!(request, decoded); } @@ -384,7 +390,7 @@ mod tests { #[test] fn encode_decode_ping_response() { let id = RequestId(vec![1]); - let request = Message::Response(Response { + let request = Payload::Response(Response { id, body: ResponseBody::Pong { enr_seq: 15, @@ -394,7 +400,7 @@ mod tests { }); let encoded = request.clone().encode(); - let decoded = Message::decode(&encoded).unwrap(); + let decoded = Payload::decode(&encoded).unwrap(); assert_eq!(request, decoded); } @@ -402,7 +408,7 @@ mod tests { #[test] fn encode_decode_find_node_request() { let id = RequestId(vec![1]); - let request = Message::Request(Request { + let request = Payload::Request(Request { id, body: RequestBody::FindNode { distances: vec![12], @@ -410,7 +416,7 @@ mod tests { }); let encoded = request.clone().encode(); - let decoded = Message::decode(&encoded).unwrap(); + let decoded = Payload::decode(&encoded).unwrap(); assert_eq!(request, decoded); } @@ -435,7 +441,7 @@ mod tests { let enr_list = vec![enr1, enr2, enr3]; let id = RequestId(vec![1]); - let request = Message::Response(Response { + let request = Payload::Response(Response { id, body: ResponseBody::Nodes { total: 1, @@ -444,7 +450,7 @@ mod tests { }); let encoded = request.clone().encode(); - let decoded = Message::decode(&encoded).unwrap(); + let decoded = Payload::decode(&encoded).unwrap(); assert_eq!(request, decoded); } @@ -452,7 +458,7 @@ mod tests { #[test] fn encode_decode_talk_request() { let id = RequestId(vec![1]); - let request = Message::Request(Request { + let request = Payload::Request(Request { id, body: RequestBody::TalkReq { protocol: vec![17u8; 32], @@ -461,7 +467,7 @@ mod tests { }); let encoded = request.clone().encode(); - let decoded = Message::decode(&encoded).unwrap(); + let decoded = Payload::decode(&encoded).unwrap(); assert_eq!(request, decoded); } diff --git a/src/rpc/notification/mod.rs b/src/rpc/notification/mod.rs index 37158b605..35c4f8c2d 100644 --- a/src/rpc/notification/mod.rs +++ b/src/rpc/notification/mod.rs @@ -1,10 +1,12 @@ use super::{REALYINIT_MSG_TYPE, REALYMSG_MSG_TYPE}; -use crate::{impl_from_variant_wrap, packet::MessageNonce}; +use crate::{ + packet::{MessageNonce, MESSAGE_NONCE_LENGTH}, + Enr, +}; use parse_display_derive::Display; -use rlp::{Decodable, DecoderError, Rlp, RlpStream}; - -mod relay_init; -mod relay_msg; +use enr::NodeId; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; +use std::fmt; pub use relay_init::RelayInit; pub use relay_msg::RelayMsg; @@ -17,15 +19,12 @@ type NonceOfTimedOutMessage = MessageNonce; pub enum Notification { /// A notification to initialise a one-shot relay circuit for hole-punching. #[display("Notification: {0}")] - RelayInit(pub Enr, pub NodeId, pub NonceOfTimedOutMessage), + RelayInit(Enr, NodeId, NonceOfTimedOutMessage), /// The notification relayed to target of hole punch attempt. #[display("Notification: {0}")] - RelayMsg(pub Enr, pub NonceOfTimedOutMessage), + RelayMsg(Enr, NonceOfTimedOutMessage), } -impl_from_variant_wrap!(, RelayInit, Notification, Self::RelayInit); -impl_from_variant_wrap!(, RelayMsg, Notification, Self::RelayMsg); - impl Notification { pub fn msg_type(&self) -> u8 { match self { @@ -41,7 +40,9 @@ impl Notification { buf.push(msg_type); let mut s = RlpStream::new(); let _ = match self { - Self::RelayInit(notif) => s.append(¬if), + Self::RelayInit(notif) => { + + }, Self::RelayMsg(notif) => s.append(¬if), }; buf.extend_from_slice(&s.out()); diff --git a/src/rpc/request/mod.rs b/src/rpc/request/mod.rs index ed2932e13..f1a29ae11 100644 --- a/src/rpc/request/mod.rs +++ b/src/rpc/request/mod.rs @@ -1,4 +1,4 @@ -use super::{FINDNODE_MSG_TYPE, PING_MSG_TYPE, TALKREQ_MSG_TYPE}; +use super::{FINDNODE_MSG_TYPE, PING_MSG_TYPE, TALKREQ_MSG_TYPE, Payload}; use crate::impl_from_tuple_struct_unwrap; use parse_display_derive::Display; use rlp::{DecoderError, Rlp, RlpStream}; @@ -18,17 +18,17 @@ pub struct Request { pub body: RequestBody, } -impl Request { - pub fn msg_type(&self) -> u8 { +impl Payload for Request { + fn msg_type(&self) -> u8 { match self.body { - RequestBody::Ping { .. } => 1, - RequestBody::FindNode { .. } => 3, - RequestBody::TalkReq { .. } => 5, + RequestBody::Ping { .. } => PING_MSG_TYPE, + RequestBody::FindNode { .. } => FINDNODE_MSG_TYPE, + RequestBody::TalkReq { .. } => TALKREQ_MSG_TYPE, } } /// Encodes a request message to RLP-encoded bytes. - pub fn encode(self) -> Vec { + fn encode(self) -> Vec { let mut buf = Vec::with_capacity(10); let msg_type = self.msg_type(); buf.push(msg_type); @@ -66,7 +66,7 @@ impl Request { } /// Decodes RLP-encoded bytes into a request message. - pub fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { + fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { let list_len = rlp.item_count()?; let id = RequestId::decode(rlp.val_at::>(0)?)?; let message = match msg_type { diff --git a/src/rpc/response/mod.rs b/src/rpc/response/mod.rs index 63bec2fef..c28a048b0 100644 --- a/src/rpc/response/mod.rs +++ b/src/rpc/response/mod.rs @@ -22,9 +22,9 @@ pub struct Response { impl Response { pub fn msg_type(&self) -> u8 { match &self.body { - ResponseBody::Pong { .. } => 2, - ResponseBody::Nodes { .. } => 4, - ResponseBody::TalkResp { .. } => 6, + ResponseBody::Pong { .. } => PONG_MSG_TYPE, + ResponseBody::Nodes { .. } => NODES_MSG_TYPE, + ResponseBody::TalkResp { .. } => TALKRESP_MSG_TYPE, } } From 2b0876ab06703f841977bbac0f9ca925e077c2f9 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 30 Apr 2023 12:44:21 +0200 Subject: [PATCH 045/154] Incorporate notification from nat_hole_punch crate --- src/handler/mod.rs | 10 +- src/handler/request_call.rs | 2 +- src/handler/sessions/crypto/mod.rs | 2 +- src/lib.rs | 1 - src/macro_rules.rs | 42 ---- src/rpc/mod.rs | 240 ++++++----------------- src/rpc/notification.rs | 108 ++++++++++ src/rpc/notification/mod.rs | 64 ------ src/rpc/notification/relay_init.rs | 75 ------- src/rpc/notification/relay_msg.rs | 60 ------ src/rpc/{request/mod.rs => request.rs} | 54 ++++- src/rpc/request/request_body.rs | 37 ---- src/rpc/{response/mod.rs => response.rs} | 93 +++++++-- src/rpc/response/response_body.rs | 54 ----- 14 files changed, 289 insertions(+), 553 deletions(-) delete mode 100644 src/macro_rules.rs create mode 100644 src/rpc/notification.rs delete mode 100644 src/rpc/notification/mod.rs delete mode 100644 src/rpc/notification/relay_init.rs delete mode 100644 src/rpc/notification/relay_msg.rs rename src/rpc/{request/mod.rs => request.rs} (78%) delete mode 100644 src/rpc/request/request_body.rs rename src/rpc/{response/mod.rs => response.rs} (75%) delete mode 100644 src/rpc/response/response_body.rs diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 8088a463a..b902e5201 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -31,7 +31,7 @@ use crate::{ discv5::PERMIT_BAN_LIST, error::{Discv5Error, RequestError}, packet::{ChallengeData, IdNonce, MessageNonce, Packet, PacketKind, ProtocolIdentity}, - rpc::{Payload, Request, RequestBody, RequestId, Response, ResponseBody}, + rpc::{Message, Payload, Request, RequestBody, RequestId, Response, ResponseBody}, socket, socket::{FilterConfig, Outbound, Socket}, Enr, @@ -991,7 +991,7 @@ impl Handler

{ ); return; } - Ok(ref bytes) => match Payload::decode(bytes) { + Ok(ref bytes) => match Message::decode(bytes) { Ok(message) => message, Err(msg_err) => { // try to decode the message as an application layer notification @@ -1008,7 +1008,7 @@ impl Handler

{ }; match message { - Payload::Response(response) => { + Message::Response(response) => { // Sessions could be awaiting an ENR response. Check if this response matches // these let Some(request_id) = session.awaiting_enr.as_ref() else { @@ -1083,7 +1083,7 @@ impl Handler

{ }; // attempt to decrypt and process the message. let message = match session.decrypt_message(message_nonce, message, authenticated_data) { - Ok(m) => match Payload::decode(&m) { + Ok(m) => match Message::decode(&m) { Ok(p) => p, Err(e) => { warn!("Failed to decode message. Error: {:?}, {}", e, node_address); @@ -1123,7 +1123,7 @@ impl Handler

{ trace!("Received message from: {}", node_address); match message { - Payload::Request(request) => { + Message::Request(request) => { // report the request to the application if let Err(e) = self .service_send diff --git a/src/handler/request_call.rs b/src/handler/request_call.rs index b91c7643e..a4f8993b9 100644 --- a/src/handler/request_call.rs +++ b/src/handler/request_call.rs @@ -1,7 +1,7 @@ pub use crate::node_info::{NodeAddress, NodeContact}; use crate::{ packet::Packet, - rpc::{Request, RequestBody}, + rpc::{Payload, Request, RequestBody}, }; use super::HandlerReqId; diff --git a/src/handler/sessions/crypto/mod.rs b/src/handler/sessions/crypto/mod.rs index b80c20d85..3ddabc984 100644 --- a/src/handler/sessions/crypto/mod.rs +++ b/src/handler/sessions/crypto/mod.rs @@ -410,7 +410,7 @@ mod tests { let message = decrypt_message(&key, nonce, &ciphertext, &auth_data).unwrap(); dbg!(&message); dbg!(hex::encode(&message)); - let rpc = crate::rpc::Payload::decode(&message).unwrap(); + let rpc = crate::rpc::Message::decode(&message).unwrap(); println!("{rpc}"); } diff --git a/src/lib.rs b/src/lib.rs index c38293a6e..72d1e5308 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,7 +110,6 @@ pub mod handler; mod ipmode; pub mod kbucket; mod lru_time_cache; -pub mod macro_rules; pub mod metrics; mod node_info; pub mod packet; diff --git a/src/macro_rules.rs b/src/macro_rules.rs deleted file mode 100644 index 9c24c2891..000000000 --- a/src/macro_rules.rs +++ /dev/null @@ -1,42 +0,0 @@ -/// Implements the From trait for an enum from some type. Nests a type in a variant with a single -/// value. Takes any generics, the type to nest, the enum and the variant. -#[macro_export] -macro_rules! impl_from_variant_wrap { - ($(<$($generic: ident$(: $trait: ident$(+ $traits: ident)*)*,)+>)*, $from_type: ty, $to_enum: ty, $to_variant: path) => { - impl$(<$($generic $(: $trait $(+ $traits)*)*,)+>)* From<$from_type> for $to_enum { - fn from(t: $from_type) -> Self { - $to_variant(t) - } - } - }; -} - -/// Implements the From trait for some type from an enum. Extracts a type nested in a variant with -/// a single value. Takes any generics, the enum, the type nested in the variant and the variant. -#[macro_export] -macro_rules! impl_from_variant_unwrap { - ($(<$($generic: ident$(: $trait: ident$(+ $traits: ident)*)*,)+>)*, $from_enum: ty, $to_type: ty, $from_variant: path) => { - impl$(<$($generic $(: $trait $(+ $traits)*)*,)+>)* From<$from_enum> for $to_type { - fn from(e: $from_enum) -> Self { - if let $from_variant(v) = e { - return v; - } - panic!("Bad impl of From") - } - } - }; -} - -/// Implements the From trait for some type from a (1-)tuple struct. Extracts a type nested in a -/// 1-tuple tuple struct. Takes any generics, the tuple struct type and the type nested in the -/// tuple struct. -#[macro_export] -macro_rules! impl_from_tuple_struct_unwrap { - ($(<$($generic: ident$(: $trait: ident$(+ $traits: ident)*)*,)+>)*, $from_tuple_struct: ty, $to_type: ty) => { - impl$(<$($generic $(: $trait $(+ $traits)*)*,)+>)* From<$from_tuple_struct> for $to_type { - fn from(s: $from_tuple_struct) -> Self { - s.0 - } - } - }; -} diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 4030d256e..8a8f9ee13 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -1,8 +1,5 @@ -use enr::{CombinedKey, Enr}; use parse_display_derive::Display; use rlp::{DecoderError, Rlp}; -use std::net::{IpAddr, Ipv6Addr}; -use tracing::{debug, warn}; mod notification; mod request; @@ -12,6 +9,7 @@ pub use notification::Notification; pub use request::{Request, RequestBody, RequestId}; pub use response::{Response, ResponseBody}; +/// Message type IDs. /// Ping notification type. pub const PING_MSG_TYPE: u8 = 1; /// Pong notification type. @@ -25,18 +23,25 @@ pub const TALKREQ_MSG_TYPE: u8 = 5; /// TalkResp notification type. pub const TALKRESP_MSG_TYPE: u8 = 6; /// RelayInit notification type. -pub const REALYINIT_MSG_TYPE: u8 = 7; +pub const RELAYINIT_MSG_TYPE: u8 = 7; /// RelayMsg notification type. -pub const REALYMSG_MSG_TYPE: u8 = 8; - -pub trait Payload where Self: Sized { +pub const RELAYMSG_MSG_TYPE: u8 = 8; + +/// The payload of message containers SessionMessage, Message or Handshake type. +pub trait Payload +where + Self: Sized, +{ + /// Matches a payload type to its message type id. fn msg_type(&self) -> u8; + /// Encodes a message to RLP-encoded bytes. fn encode(self) -> Vec; + /// Decodes RLP-encoded bytes into a message. fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result; } #[derive(Debug, Clone, PartialEq, Eq, Display)] -/// A combined type representing the packet payloads. +/// A combined type representing the messages which are the payloads of packets. pub enum Message { /// A request, which contains its [`RequestId`]. #[display("{0}")] @@ -49,6 +54,20 @@ pub enum Message { Notification(Notification), } +macro_rules! impl_from_variant_wrap { + ($from_type: ty, $to_type: ty, $variant: path) => { + impl From<$from_type> for $to_type { + fn from(t: $from_type) -> Self { + $variant(t) + } + } + }; +} + +impl_from_variant_wrap!(Request, Message, Self::Request); +impl_from_variant_wrap!(Response, Message, Self::Response); +impl_from_variant_wrap!(Notification, Message, Self::Notification); + #[allow(dead_code)] impl Message { pub fn encode(self) -> Vec { @@ -63,170 +82,21 @@ impl Message { if data.len() < 3 { return Err(DecoderError::RlpIsTooShort); } - let msg_type = data[0]; - let rlp = rlp::Rlp::new(&data[1..]); - let list_len = rlp.item_count().and_then(|size| { - if size < 2 { - Err(DecoderError::RlpIncorrectListLen) - } else { - Ok(size) - } - })?; - - let id = RequestId::decode(rlp.val_at::>(0)?)?; - - let message = match msg_type { - 1 => { - // PingRequest - if list_len != 2 { - debug!( - "Ping Request has an invalid RLP list length. Expected 2, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - Payload::Request(Request { - id, - body: RequestBody::Ping { - enr_seq: rlp.val_at::(1)?, - }, - }) - } - 2 => { - // PingResponse - if list_len != 4 { - debug!( - "Ping Response has an invalid RLP list length. Expected 4, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - let ip_bytes = rlp.val_at::>(2)?; - let ip = match ip_bytes.len() { - 4 => { - let mut ip = [0u8; 4]; - ip.copy_from_slice(&ip_bytes); - IpAddr::from(ip) - } - 16 => { - let mut ip = [0u8; 16]; - ip.copy_from_slice(&ip_bytes); - let ipv6 = Ipv6Addr::from(ip); - // If the ipv6 is ipv4 compatible/mapped, simply return the ipv4. - if let Some(ipv4) = ipv6.to_ipv4() { - IpAddr::V4(ipv4) - } else { - IpAddr::V6(ipv6) - } - } - _ => { - debug!("Ping Response has incorrect byte length for IP"); - return Err(DecoderError::RlpIncorrectListLen); - } - }; - let port = rlp.val_at::(3)?; - Payload::Response(Response { - id, - body: ResponseBody::Pong { - enr_seq: rlp.val_at::(1)?, - ip, - port, - }, - }) - } - 3 => { - // FindNodeRequest - if list_len != 2 { - debug!( - "FindNode Request has an invalid RLP list length. Expected 2, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - let distances = rlp.list_at::(1)?; - - for distance in distances.iter() { - if distance > &256u64 { - warn!( - "Rejected FindNode request asking for unknown distance {}, maximum 256", - distance - ); - return Err(DecoderError::Custom("FINDNODE request distance invalid")); - } - } - - Payload::Request(Request { - id, - body: RequestBody::FindNode { distances }, - }) - } - 4 => { - // NodesResponse - if list_len != 3 { - debug!( - "Nodes Response has an invalid RLP list length. Expected 3, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - - let nodes = { - let enr_list_rlp = rlp.at(2)?; - if enr_list_rlp.is_empty() { - // no records - vec![] - } else { - enr_list_rlp.as_list::>()? - } - }; - Payload::Response(Response { - id, - body: ResponseBody::Nodes { - total: rlp.val_at::(1)?, - nodes, - }, - }) - } - 5 => { - // Talk Request - if list_len != 3 { - debug!( - "Talk Request has an invalid RLP list length. Expected 3, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - let protocol = rlp.val_at::>(1)?; - let request = rlp.val_at::>(2)?; - Payload::Request(Request { - id, - body: RequestBody::TalkReq { protocol, request }, - }) + match msg_type { + PING_MSG_TYPE | FINDNODE_MSG_TYPE | TALKREQ_MSG_TYPE => { + Ok(Request::decode(msg_type, &rlp)?.into()) } - 6 => { - // Talk Response - if list_len != 2 { - debug!( - "Talk Response has an invalid RLP list length. Expected 2, found {}", - list_len - ); - return Err(DecoderError::RlpIncorrectListLen); - } - let response = rlp.val_at::>(1)?; - Payload::Response(Response { - id, - body: ResponseBody::TalkResp { response }, - }) + PONG_MSG_TYPE | NODES_MSG_TYPE | TALKRESP_MSG_TYPE => { + Ok(Response::decode(msg_type, &rlp)?.into()) } - _ => { - return Err(DecoderError::Custom("Unknown RPC message type")); + RELAYINIT_MSG_TYPE | RELAYMSG_MSG_TYPE => { + Ok(Notification::decode(msg_type, &rlp)?.into()) } - }; - - Ok(message) + _ => Err(DecoderError::Custom("Unknown RPC message type")), + } } } @@ -234,13 +104,15 @@ impl Message { mod tests { use super::*; use enr::EnrBuilder; + use enr::{CombinedKey, Enr}; + use std::net::IpAddr; #[test] fn ref_test_encode_request_ping() { // reference input let id = RequestId(vec![1]); let enr_seq = 1; - let message = Payload::Request(Request { + let message = Message::Request(Request { id, body: RequestBody::Ping { enr_seq }, }); @@ -257,7 +129,7 @@ mod tests { // reference input let id = RequestId(vec![1]); let distances = vec![256]; - let message = Payload::Request(Request { + let message = Message::Request(Request { id, body: RequestBody::FindNode { distances }, }); @@ -276,7 +148,7 @@ mod tests { let enr_seq = 1; let ip: IpAddr = "127.0.0.1".parse().unwrap(); let port = 5000; - let message = Payload::Response(Response { + let message = Message::Response(Response { id, body: ResponseBody::Pong { enr_seq, ip, port }, }); @@ -297,7 +169,7 @@ mod tests { // expected hex output let expected_output = hex::decode("04c30101c0").unwrap(); - let message = Payload::Response(Response { + let message = Message::Response(Response { id, body: ResponseBody::Nodes { total, @@ -317,7 +189,7 @@ mod tests { // expected hex output let expected_output = hex::decode("04f87b0101f877f875b84028df8ee09f4a62091f1a8b61f18aad2cfe3eadc6f350d527077f9aebc56098883a47f46c6b49bbb91954238f9e14933277b2bd1e0c45b1771b94cd078c32dacb0182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138").unwrap(); - let message = Payload::Response(Response { + let message = Message::Response(Response { id, body: ResponseBody::Nodes { total, @@ -340,7 +212,7 @@ mod tests { // expected hex output let expected_output = hex::decode("04f8f20101f8eef875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235").unwrap(); - let message = Payload::Response(Response { + let message = Message::Response(Response { id, body: ResponseBody::Nodes { total, @@ -358,10 +230,10 @@ mod tests { let expected_enr1 = "enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg".parse::>().unwrap(); let expected_enr2 = "enr:-HW4QNfxw543Ypf4HXKXdYxkyzfcxcO-6p9X986WldfVpnVTQX1xlTnWrktEWUbeTZnmgOuAY_KUhbVV1Ft98WoYUBMBgmlkgnY0iXNlY3AyNTZrMaEDDiy3QkHAxPyOgWbxp5oF1bDdlYE6dLCUUp8xfVw50jU".parse::>().unwrap(); - let decoded = Payload::decode(&input).unwrap(); + let decoded = Message::decode(&input).unwrap(); match decoded { - Payload::Response(response) => match response.body { + Message::Response(response) => match response.body { ResponseBody::Nodes { total, nodes } => { assert_eq!(total, 1); assert_eq!(nodes[0], expected_enr1); @@ -376,13 +248,13 @@ mod tests { #[test] fn encode_decode_ping_request() { let id = RequestId(vec![1]); - let request = Payload::Request(Request { + let request = Message::Request(Request { id, body: RequestBody::Ping { enr_seq: 15 }, }); let encoded = request.clone().encode(); - let decoded = Payload::decode(&encoded).unwrap(); + let decoded = Message::decode(&encoded).unwrap(); assert_eq!(request, decoded); } @@ -390,7 +262,7 @@ mod tests { #[test] fn encode_decode_ping_response() { let id = RequestId(vec![1]); - let request = Payload::Response(Response { + let request = Message::Response(Response { id, body: ResponseBody::Pong { enr_seq: 15, @@ -400,7 +272,7 @@ mod tests { }); let encoded = request.clone().encode(); - let decoded = Payload::decode(&encoded).unwrap(); + let decoded = Message::decode(&encoded).unwrap(); assert_eq!(request, decoded); } @@ -408,7 +280,7 @@ mod tests { #[test] fn encode_decode_find_node_request() { let id = RequestId(vec![1]); - let request = Payload::Request(Request { + let request = Message::Request(Request { id, body: RequestBody::FindNode { distances: vec![12], @@ -416,7 +288,7 @@ mod tests { }); let encoded = request.clone().encode(); - let decoded = Payload::decode(&encoded).unwrap(); + let decoded = Message::decode(&encoded).unwrap(); assert_eq!(request, decoded); } @@ -441,7 +313,7 @@ mod tests { let enr_list = vec![enr1, enr2, enr3]; let id = RequestId(vec![1]); - let request = Payload::Response(Response { + let request = Message::Response(Response { id, body: ResponseBody::Nodes { total: 1, @@ -450,7 +322,7 @@ mod tests { }); let encoded = request.clone().encode(); - let decoded = Payload::decode(&encoded).unwrap(); + let decoded = Message::decode(&encoded).unwrap(); assert_eq!(request, decoded); } @@ -458,7 +330,7 @@ mod tests { #[test] fn encode_decode_talk_request() { let id = RequestId(vec![1]); - let request = Payload::Request(Request { + let request = Message::Request(Request { id, body: RequestBody::TalkReq { protocol: vec![17u8; 32], @@ -467,7 +339,7 @@ mod tests { }); let encoded = request.clone().encode(); - let decoded = Payload::decode(&encoded).unwrap(); + let decoded = Message::decode(&encoded).unwrap(); assert_eq!(request, decoded); } diff --git a/src/rpc/notification.rs b/src/rpc/notification.rs new file mode 100644 index 000000000..22c86614b --- /dev/null +++ b/src/rpc/notification.rs @@ -0,0 +1,108 @@ +use super::{Payload, RELAYINIT_MSG_TYPE, RELAYMSG_MSG_TYPE}; +use crate::{ + packet::{MessageNonce, MESSAGE_NONCE_LENGTH}, + Enr, +}; +use enr::NodeId; +use parse_display_derive::Display; +use rlp::{DecoderError, Rlp, RlpStream}; + +/// Nonce of request that triggered the initiation of this hole punching attempt. +type NonceOfTimedOutMessage = MessageNonce; +/// Node id length in bytes. +pub const NODE_ID_LENGTH: usize = 32; + +/// A unicast notification sent over discv5. +#[derive(Debug, Display, PartialEq, Eq, Clone)] +pub enum Notification { + /// A notification to initialise a one-shot relay circuit for hole-punching. + #[display("Notification: RelayInit: Initiator: {0}, Target: {1}, Nonce: {:2}")] + RelayInit(Enr, NodeId, NonceOfTimedOutMessage), + /// The notification relayed to target of hole punch attempt. + #[display("Notification: RelayMsg: Initiator: {0}, Nonce: {:1}")] + RelayMsg(Enr, NonceOfTimedOutMessage), +} + +impl Payload for Notification { + /// Matches a notification type to its message type id. + fn msg_type(&self) -> u8 { + match self { + Self::RelayInit(..) => RELAYINIT_MSG_TYPE, + Self::RelayMsg(..) => RELAYMSG_MSG_TYPE, + } + } + + /// Encodes a notification message to RLP-encoded bytes. + fn encode(self) -> Vec { + let mut buf = Vec::with_capacity(100); + let msg_type = self.msg_type(); + buf.push(msg_type); + let mut s = RlpStream::new(); + match self { + Self::RelayInit(initiator, target, nonce) => { + s.begin_list(3); + s.append(&initiator); + s.append(&(&target.raw() as &[u8])); + s.append(&(&nonce as &[u8])); + } + Self::RelayMsg(initiator, nonce) => { + s.begin_list(2); + s.append(&initiator); + s.append(&(&nonce as &[u8])); + } + } + buf.extend_from_slice(&s.out()); + buf + } + + /// Decodes RLP-encoded bytes into a notification message. + fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { + match msg_type { + RELAYINIT_MSG_TYPE => { + if rlp.item_count()? != 3 { + return Err(DecoderError::RlpIncorrectListLen); + } + let initiator = rlp.val_at::(0)?; + + let tgt_bytes = rlp.val_at::>(1)?; + if tgt_bytes.len() > NODE_ID_LENGTH { + return Err(DecoderError::RlpIsTooBig); + } + let mut tgt = [0u8; NODE_ID_LENGTH]; + tgt[NODE_ID_LENGTH - tgt_bytes.len()..].copy_from_slice(&tgt_bytes); + let tgt = NodeId::from(tgt); + + let nonce = { + let bytes = rlp.val_at::>(2)?; + if bytes.len() > MESSAGE_NONCE_LENGTH { + return Err(DecoderError::RlpIsTooBig); + } + let mut buf = [0u8; MESSAGE_NONCE_LENGTH]; + buf[MESSAGE_NONCE_LENGTH - bytes.len()..].copy_from_slice(&bytes); + buf + }; + + Ok(Notification::RelayInit(initiator, tgt, nonce)) + } + RELAYMSG_MSG_TYPE => { + if rlp.item_count()? != 2 { + return Err(DecoderError::RlpIncorrectListLen); + } + let initiator = rlp.val_at::(0)?; + + let nonce = { + let bytes = rlp.val_at::>(1)?; + if bytes.len() > MESSAGE_NONCE_LENGTH { + return Err(DecoderError::RlpIsTooBig); + } + let mut buf = [0u8; MESSAGE_NONCE_LENGTH]; + buf[MESSAGE_NONCE_LENGTH - bytes.len()..].copy_from_slice(&bytes); + buf + }; + + Ok(Notification::RelayMsg(initiator, nonce)) + } + _ => unreachable!("Implementation does not adhere to wire protocol"), + } + } +} diff --git a/src/rpc/notification/mod.rs b/src/rpc/notification/mod.rs deleted file mode 100644 index 35c4f8c2d..000000000 --- a/src/rpc/notification/mod.rs +++ /dev/null @@ -1,64 +0,0 @@ -use super::{REALYINIT_MSG_TYPE, REALYMSG_MSG_TYPE}; -use crate::{ - packet::{MessageNonce, MESSAGE_NONCE_LENGTH}, - Enr, -}; -use parse_display_derive::Display; -use enr::NodeId; -use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; -use std::fmt; - -pub use relay_init::RelayInit; -pub use relay_msg::RelayMsg; - -/// Nonce of request that triggered the initiation of this hole punching attempt. -type NonceOfTimedOutMessage = MessageNonce; - -/// A unicast notification sent over discv5. -#[derive(Debug, Display, PartialEq, Eq, Clone)] -pub enum Notification { - /// A notification to initialise a one-shot relay circuit for hole-punching. - #[display("Notification: {0}")] - RelayInit(Enr, NodeId, NonceOfTimedOutMessage), - /// The notification relayed to target of hole punch attempt. - #[display("Notification: {0}")] - RelayMsg(Enr, NonceOfTimedOutMessage), -} - -impl Notification { - pub fn msg_type(&self) -> u8 { - match self { - Self::RelayInit(..) => 7, - Self::RelayMsg(..) => 8, - } - } - - /// Encodes a notification message to RLP-encoded bytes. - pub fn encode(self) -> Vec { - let mut buf = Vec::with_capacity(100); - let msg_type = self.msg_type(); - buf.push(msg_type); - let mut s = RlpStream::new(); - let _ = match self { - Self::RelayInit(notif) => { - - }, - Self::RelayMsg(notif) => s.append(¬if), - }; - buf.extend_from_slice(&s.out()); - buf - } - - /// Decodes RLP-encoded bytes into a notification message. - pub fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { - match msg_type { - REALYINIT_MSG_TYPE => Ok(RelayInit::decode(rlp)?.into()), - REALYMSG_MSG_TYPE => Ok(RelayMsg::decode(rlp)?.into()), - _ => { - return Err(DecoderError::Custom( - "Unknown RPC notification message type", - )) - } - } - } -} diff --git a/src/rpc/notification/relay_init.rs b/src/rpc/notification/relay_init.rs deleted file mode 100644 index 1aed6d35b..000000000 --- a/src/rpc/notification/relay_init.rs +++ /dev/null @@ -1,75 +0,0 @@ -use super::Notification; -use crate::{ - impl_from_variant_unwrap, - packet::{MessageNonce, MESSAGE_NONCE_LENGTH}, - Enr, -}; -use enr::NodeId; -use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; -use std::fmt; - -/// Node id length in bytes. -const NODE_ID_LENGTH: usize = 32; -/// Nonce of request that triggered the initiation of this hole punching attempt. -type NonceOfTimedOutMessage = MessageNonce; - -/// A notification sent from the initiator to the relay. Contains the enr of the initiator, the -/// nonce of the timed out request and the node id of the target. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct RelayInit(pub Enr, pub NodeId, pub NonceOfTimedOutMessage); - -impl_from_variant_unwrap!(, Notification, RelayInit, Notification::RelayInit); - -impl Encodable for RelayInit { - fn rlp_append(&self, s: &mut RlpStream) { - let RelayInit(initiator, target, nonce) = self; - - s.begin_list(3); - s.append(initiator); - s.append(&(&target.raw() as &[u8])); - s.append(&(nonce as &[u8])); - } -} - -impl Decodable for RelayInit { - fn decode(rlp: &Rlp<'_>) -> Result { - if rlp.item_count()? != 3 { - return Err(DecoderError::RlpIncorrectListLen); - } - let initiator = rlp.val_at::(0)?; - - let tgt_bytes = rlp.val_at::>(1)?; - if tgt_bytes.len() > NODE_ID_LENGTH { - return Err(DecoderError::RlpIsTooBig); - } - let mut tgt = [0u8; NODE_ID_LENGTH]; - tgt[NODE_ID_LENGTH - tgt_bytes.len()..].copy_from_slice(&tgt_bytes); - let tgt = NodeId::from(tgt); - - let nonce_bytes = rlp.val_at::>(2)?; - if nonce_bytes.len() > MESSAGE_NONCE_LENGTH { - return Err(DecoderError::RlpIsTooBig); - } - let mut nonce = [0u8; MESSAGE_NONCE_LENGTH]; - nonce[MESSAGE_NONCE_LENGTH - nonce_bytes.len()..].copy_from_slice(&nonce_bytes); - - Ok(RelayInit(initiator, tgt, nonce)) - } -} - -impl fmt::Display for RelayInit { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let initiator = &self.0; - let tgt = hex::encode(self.1); - let nonce = hex::encode(self.2); - write!( - f, - "RelayInit: Initiator: {}, Target: 0x{}..{}, Nonce: 0x{}..{}", - initiator, - &tgt[0..4], - &tgt[tgt.len() - 4..], - &nonce[0..2], - &nonce[nonce.len() - 2..] - ) - } -} diff --git a/src/rpc/notification/relay_msg.rs b/src/rpc/notification/relay_msg.rs deleted file mode 100644 index 0c7bff82d..000000000 --- a/src/rpc/notification/relay_msg.rs +++ /dev/null @@ -1,60 +0,0 @@ -use super::Notification; -use crate::{ - impl_from_variant_unwrap, - packet::{MessageNonce, MESSAGE_NONCE_LENGTH}, - Enr, -}; -use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; -use std::fmt; - -// Nonce of request that triggered the initiation of this hole punching attempt. -type NonceOfTimedOutMessage = MessageNonce; - -/// A notification sent from the initiator to the relay. Contains the enr of the initiator and the -/// nonce of the timed out request. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct RelayMsg(pub Enr, pub NonceOfTimedOutMessage); - -impl_from_variant_unwrap!(, Notification, RelayMsg, Notification::RelayMsg); - -impl Encodable for RelayMsg { - fn rlp_append(&self, s: &mut RlpStream) { - let RelayMsg(initiator, nonce) = self; - - s.begin_list(2); - s.append(initiator); - s.append(&(nonce as &[u8])); - } -} - -impl Decodable for RelayMsg { - fn decode(rlp: &Rlp<'_>) -> Result { - if rlp.item_count()? != 2 { - return Err(DecoderError::RlpIncorrectListLen); - } - let initiator = rlp.val_at::(0)?; - - let nonce_bytes = rlp.val_at::>(2)?; - if nonce_bytes.len() > MESSAGE_NONCE_LENGTH { - return Err(DecoderError::RlpIsTooBig); - } - let mut nonce = [0u8; MESSAGE_NONCE_LENGTH]; - nonce[MESSAGE_NONCE_LENGTH - nonce_bytes.len()..].copy_from_slice(&nonce_bytes); - - Ok(RelayMsg(initiator, nonce).into()) - } -} - -impl fmt::Display for RelayMsg { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let initiator = &self.0; - let nonce = hex::encode(self.1); - write!( - f, - "RelayMsg: Initiator: {}, Nonce: 0x{}..{}", - initiator, - &nonce[0..2], - &nonce[nonce.len() - 2..] - ) - } -} diff --git a/src/rpc/request/mod.rs b/src/rpc/request.rs similarity index 78% rename from src/rpc/request/mod.rs rename to src/rpc/request.rs index f1a29ae11..78516e994 100644 --- a/src/rpc/request/mod.rs +++ b/src/rpc/request.rs @@ -1,13 +1,8 @@ -use super::{FINDNODE_MSG_TYPE, PING_MSG_TYPE, TALKREQ_MSG_TYPE, Payload}; -use crate::impl_from_tuple_struct_unwrap; +use super::{Payload, FINDNODE_MSG_TYPE, PING_MSG_TYPE, TALKREQ_MSG_TYPE}; use parse_display_derive::Display; use rlp::{DecoderError, Rlp, RlpStream}; use tracing::{debug, warn}; -mod request_body; - -pub use request_body::RequestBody; - /// A request sent between nodes. #[derive(Debug, Clone, PartialEq, Eq, Display)] #[display("Request: id: {id}: {body}")] @@ -19,6 +14,7 @@ pub struct Request { } impl Payload for Request { + /// Matches a request type to its message type id. fn msg_type(&self) -> u8 { match self.body { RequestBody::Ping { .. } => PING_MSG_TYPE, @@ -128,17 +124,59 @@ impl Payload for Request { body: RequestBody::TalkReq { protocol, request }, } } - _ => return Err(DecoderError::Custom("Unknown RPC request message type")), + _ => unreachable!("Implementation does not adhere to wire protocol"), }; Ok(message) } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RequestBody { + /// A PING request. + Ping { + /// Our current ENR sequence number. + enr_seq: u64, + }, + /// A FINDNODE request. + FindNode { + /// The distance(s) of peers we expect to be returned in the response. + distances: Vec, + }, + /// A Talk request. + TalkReq { + /// The protocol requesting. + protocol: Vec, + /// The request. + request: Vec, + }, +} + +impl std::fmt::Display for RequestBody { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + RequestBody::Ping { enr_seq } => write!(f, "PING: enr_seq: {enr_seq}"), + RequestBody::FindNode { distances } => { + write!(f, "FINDNODE Request: distance: {distances:?}") + } + RequestBody::TalkReq { protocol, request } => write!( + f, + "TALK: protocol: {}, request: {}", + hex::encode(protocol), + hex::encode(request) + ), + } + } +} + /// Type to manage the request IDs. #[derive(Debug, Clone, PartialEq, Hash, Eq)] pub struct RequestId(pub Vec); -impl_from_tuple_struct_unwrap!(, RequestId, Vec); +impl From> for RequestId { + fn from(v: Vec) -> Self { + RequestId(v) + } +} impl RequestId { /// Decodes the ID from a raw bytes. diff --git a/src/rpc/request/request_body.rs b/src/rpc/request/request_body.rs deleted file mode 100644 index 08ae058de..000000000 --- a/src/rpc/request/request_body.rs +++ /dev/null @@ -1,37 +0,0 @@ -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum RequestBody { - /// A PING request. - Ping { - /// Our current ENR sequence number. - enr_seq: u64, - }, - /// A FINDNODE request. - FindNode { - /// The distance(s) of peers we expect to be returned in the response. - distances: Vec, - }, - /// A Talk request. - TalkReq { - /// The protocol requesting. - protocol: Vec, - /// The request. - request: Vec, - }, -} - -impl std::fmt::Display for RequestBody { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - RequestBody::Ping { enr_seq } => write!(f, "PING: enr_seq: {enr_seq}"), - RequestBody::FindNode { distances } => { - write!(f, "FINDNODE Request: distance: {distances:?}") - } - RequestBody::TalkReq { protocol, request } => write!( - f, - "TALK: protocol: {}, request: {}", - hex::encode(protocol), - hex::encode(request) - ), - } - } -} diff --git a/src/rpc/response/mod.rs b/src/rpc/response.rs similarity index 75% rename from src/rpc/response/mod.rs rename to src/rpc/response.rs index c28a048b0..de9093bbe 100644 --- a/src/rpc/response/mod.rs +++ b/src/rpc/response.rs @@ -1,14 +1,10 @@ -use super::{RequestBody, RequestId, NODES_MSG_TYPE, PONG_MSG_TYPE, TALKRESP_MSG_TYPE}; +use super::{Payload, RequestBody, RequestId, NODES_MSG_TYPE, PONG_MSG_TYPE, TALKRESP_MSG_TYPE}; use crate::Enr; use parse_display_derive::Display; use rlp::{DecoderError, Rlp, RlpStream}; use std::net::{IpAddr, Ipv6Addr}; use tracing::debug; -mod response_body; - -pub use response_body::ResponseBody; - /// A response sent in response to a [`super::Request`] #[derive(Debug, Clone, PartialEq, Eq, Display)] #[display("Response: id: {id}: {body}")] @@ -19,8 +15,9 @@ pub struct Response { pub body: ResponseBody, } -impl Response { - pub fn msg_type(&self) -> u8 { +impl Payload for Response { + /// Matches a response type to its message type id. + fn msg_type(&self) -> u8 { match &self.body { ResponseBody::Pong { .. } => PONG_MSG_TYPE, ResponseBody::Nodes { .. } => NODES_MSG_TYPE, @@ -28,19 +25,8 @@ impl Response { } } - /// Determines if the response is a valid response to the given request. - pub fn match_request(&self, req: &RequestBody) -> bool { - match self.body { - ResponseBody::Pong { .. } => matches!(req, RequestBody::Ping { .. }), - ResponseBody::Nodes { .. } => { - matches!(req, RequestBody::FindNode { .. }) - } - ResponseBody::TalkResp { .. } => matches!(req, RequestBody::TalkReq { .. }), - } - } - /// Encodes a response message to RLP-encoded bytes. - pub fn encode(self) -> Vec { + fn encode(self) -> Vec { let mut buf = Vec::with_capacity(10); let msg_type = self.msg_type(); buf.push(msg_type); @@ -88,7 +74,7 @@ impl Response { } /// Decodes RLP-encoded bytes into a response message. - pub fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { + fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { let list_len = rlp.item_count()?; let id = RequestId::decode(rlp.val_at::>(0)?)?; let response = match msg_type { @@ -176,8 +162,73 @@ impl Response { body: ResponseBody::TalkResp { response }, } } - _ => return Err(DecoderError::Custom("Unknown RPC response message type")), + _ => unreachable!("Implementation does not adhere to wire protocol"), }; Ok(response) } } + +impl Response { + /// Determines if the response is a valid response to the given request. + pub fn match_request(&self, req: &RequestBody) -> bool { + match self.body { + ResponseBody::Pong { .. } => matches!(req, RequestBody::Ping { .. }), + ResponseBody::Nodes { .. } => { + matches!(req, RequestBody::FindNode { .. }) + } + ResponseBody::TalkResp { .. } => matches!(req, RequestBody::TalkReq { .. }), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ResponseBody { + /// A PONG response. + Pong { + /// The current ENR sequence number of the responder. + enr_seq: u64, + /// Our external IP address as observed by the responder. + ip: IpAddr, + /// Our external UDP port as observed by the responder. + port: u16, + }, + /// A NODES response. + Nodes { + /// The total number of responses that make up this response. + total: u64, + /// A list of ENR's returned by the responder. + nodes: Vec, + }, + /// The TALK response. + TalkResp { + /// The response for the talk. + response: Vec, + }, +} + +impl std::fmt::Display for ResponseBody { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ResponseBody::Pong { enr_seq, ip, port } => { + write!(f, "PONG: Enr-seq: {enr_seq}, Ip: {ip:?}, Port: {port}") + } + ResponseBody::Nodes { total, nodes } => { + write!(f, "NODES: total: {total}, Nodes: [")?; + let mut first = true; + for id in nodes { + if !first { + write!(f, ", {id}")?; + } else { + write!(f, "{id}")?; + } + first = false; + } + + write!(f, "]") + } + ResponseBody::TalkResp { response } => { + write!(f, "Response: Response {}", hex::encode(response)) + } + } + } +} diff --git a/src/rpc/response/response_body.rs b/src/rpc/response/response_body.rs deleted file mode 100644 index e3dab037a..000000000 --- a/src/rpc/response/response_body.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::Enr; -use std::net::IpAddr; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ResponseBody { - /// A PONG response. - Pong { - /// The current ENR sequence number of the responder. - enr_seq: u64, - /// Our external IP address as observed by the responder. - ip: IpAddr, - /// Our external UDP port as observed by the responder. - port: u16, - }, - /// A NODES response. - Nodes { - /// The total number of responses that make up this response. - total: u64, - /// A list of ENR's returned by the responder. - nodes: Vec, - }, - /// The TALK response. - TalkResp { - /// The response for the talk. - response: Vec, - }, -} - -impl std::fmt::Display for ResponseBody { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ResponseBody::Pong { enr_seq, ip, port } => { - write!(f, "PONG: Enr-seq: {enr_seq}, Ip: {ip:?}, Port: {port}") - } - ResponseBody::Nodes { total, nodes } => { - write!(f, "NODES: total: {total}, Nodes: [")?; - let mut first = true; - for id in nodes { - if !first { - write!(f, ", {id}")?; - } else { - write!(f, "{id}")?; - } - first = false; - } - - write!(f, "]") - } - ResponseBody::TalkResp { response } => { - write!(f, "Response: Response {}", hex::encode(response)) - } - } - } -} From 051921db560a1cb3f91d0b5b7f3ce3f2afe94ce1 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 30 Apr 2023 15:00:29 +0200 Subject: [PATCH 046/154] Move hole punch trait from nat_hole_punch crate into crate --- Cargo.toml | 2 +- src/handler/mod.rs | 148 ++++++++++++++++---------------- src/handler/nat_hole_puncher.rs | 98 +++++++++++++++++++-- src/lib.rs | 1 + src/macro_utils.rs | 11 +++ src/rpc/mod.rs | 11 +-- src/socket/send.rs | 17 +++- 7 files changed, 190 insertions(+), 98 deletions(-) create mode 100644 src/macro_utils.rs diff --git a/Cargo.toml b/Cargo.toml index 7a7053650..b6fdc6c75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ categories = ["network-programming", "asynchronous"] exclude = [".gitignore", ".github/*"] [dependencies] -nat_hole_punch = { package = "nat_hole_punch", git = "https://github.com/emhane/nat_hole_punch", branch = "main" } enr = { version = "0.7.0", features = ["k256", "ed25519"] } tokio = { version = "1.15.0", features = ["net", "sync", "macros", "rt"] } tokio-stream = "0.1.8" @@ -44,6 +43,7 @@ async-trait = "0.1.67" tokio-context = "0.1.3" mio = "0.8.6" parse-display-derive = "0.8.0" +thiserror = "1.0.40" [dev-dependencies] rand_07 = { package = "rand", version = "0.7" } diff --git a/src/handler/mod.rs b/src/handler/mod.rs index b902e5201..2be6aaf96 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -31,7 +31,9 @@ use crate::{ discv5::PERMIT_BAN_LIST, error::{Discv5Error, RequestError}, packet::{ChallengeData, IdNonce, MessageNonce, Packet, PacketKind, ProtocolIdentity}, - rpc::{Message, Payload, Request, RequestBody, RequestId, Response, ResponseBody}, + rpc::{ + Message, Notification, Payload, Request, RequestBody, RequestId, Response, ResponseBody, + }, socket, socket::{FilterConfig, Outbound, Socket}, Enr, @@ -40,7 +42,6 @@ use async_trait::async_trait; use delay_map::HashMapDelay; use enr::{CombinedKey, NodeId}; use futures::prelude::*; -use nat_hole_punch::*; use parking_lot::RwLock; use std::{ collections::HashMap, @@ -66,7 +67,7 @@ use crate::metrics::METRICS; pub use crate::node_info::{NodeAddress, NodeContact}; use active_requests::ActiveRequests; -use nat_hole_puncher::NatHolePuncher; +use nat_hole_puncher::{Error as HolePunchError, NatHolePunch, NatHolePuncher}; use request_call::RequestCall; use sessions::{Session, Sessions}; @@ -105,8 +106,9 @@ pub enum HandlerIn { /// be returned here to submit the application's response. WhoAreYou(WhoAreYouRef, Option), - /// Response to a [`HandlerOut::FindHolePunchEnr`]. Returns the ENR if it was found. - HolePunchEnr(Option, RelayMsg), + /// Response to a [`HandlerOut::FindHolePunchEnr`]. Returns the ENR if it was found. Also + /// returns the [`Notification::RelayMsg`] we intend to relay to that peer if it was found. + HolePunchEnr(Option, Notification), /// Observed socket has been update. The old socket and the current socket. SocketUpdate(Option, SocketAddr), @@ -138,9 +140,10 @@ pub enum HandlerOut { RequestFailed(RequestId, RequestError), /// A peer has supposed we have passed it another peer in a NODES response, if that is true - /// (very probably not false) the ENR of that peer is returned in a - /// [`HandlerIn::HolePunchEnr`]. - FindHolePunchEnr(NodeId, RelayMsg), + /// (very probably not false) then the ENR of that peer is returned in a + /// [`HandlerIn::HolePunchEnr`]. Holds the NodeId to look up and the + /// [`Notification::RelayMsg`] we intend to relay to that peer if we find it. + FindHolePunchEnr(NodeId, Notification), } /// How we connected to the node. @@ -328,7 +331,7 @@ impl Handler

{ HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge(wru_ref, enr).await, HandlerIn::HolePunchEnr(tgt_enr, relay_msg_notif) => { if let Err(e) = self.on_hole_punch_tgt_enr(tgt_enr, relay_msg_notif).await { - warn!("Failed to realy. Error: {}", e); + warn!("Failed to relay. Error: {}", e); } } HandlerIn::SocketUpdate(old_socket, socket) => { @@ -993,15 +996,11 @@ impl Handler

{ } Ok(ref bytes) => match Message::decode(bytes) { Ok(message) => message, - Err(msg_err) => { - // try to decode the message as an application layer notification - if let Err(notif_err) = self.on_notification(bytes).await { - warn!( - "Failed to decode as message and notification. Error: {:?}, {}, {}", - msg_err, notif_err, node_address - ); - return; - } + Err(err) => { + warn!( + "Failed to decode message. Error: {:?}, {}", + err, node_address + ); return; } }, @@ -1045,6 +1044,19 @@ impl Handler

{ // Handle standard responses self.handle_response(node_address, response).await; } + Message::Notification(notif) => { + let res = match notif { + Notification::RelayInit(initr, tgt, timed_out_nonce) => { + self.on_relay_init(initr, tgt, timed_out_nonce).await + } + Notification::RelayMsg(initr, timed_out_nonce) => { + self.on_relay_msg(initr, timed_out_nonce).await + } + }; + if let Err(e) = res { + warn!("failed handling notification from {node_address}, {e}"); + } + } _ => { warn!( "Peer sent message type that shouldn't be sent in packet type Message, {}", @@ -1324,17 +1336,16 @@ impl Handler

{ /// Assembles and sends a [`Packet`]. async fn send(&mut self, node_address: NodeAddress, packet: Packet) { - let socket_addr = node_address.socket_addr; let outbound_packet = socket::OutboundPacket { node_address, packet, }; - self.send_outbound(socket_addr, outbound_packet.into()) - .await; + self.send_outbound(outbound_packet.into()).await; } /// Sends a packet to the send handler to be encoded and sent. - async fn send_outbound(&mut self, dst: SocketAddr, packet: Outbound) { + async fn send_outbound(&mut self, packet: Outbound) { + let dst = *packet.dst(); if let Err(e) = self.socket.send.send(packet).await { warn!("Failed to send outbound packet {}", e) } @@ -1356,15 +1367,15 @@ impl Handler

{ async fn on_hole_punch_tgt_enr( &mut self, tgt_enr: Option, - relay_msg_notif: RelayMsg, - ) -> Result<(), HolePunchError> { + relay_msg_notif: Notification, + ) -> Result<(), HolePunchError> { let Some(tgt_enr) = tgt_enr else { - return Err(HolePunchError::RelayError(Discv5Error::Custom("Target enr not found"))); + return Err(HolePunchError::Relay(Discv5Error::Custom("Target enr not found"))); }; let tgt_node_address = match NodeContact::try_from_enr(tgt_enr, self.nat_hole_puncher.ip_mode) { Ok(contact) => contact.node_address(), - Err(e) => return Err(HolePunchError::RelayError(e.into())), + Err(e) => return Err(HolePunchError::Relay(e.into())), }; if let Some(session) = self.sessions.cache.get_mut(&tgt_node_address) { trace!( @@ -1373,14 +1384,13 @@ impl Handler

{ relay_msg_notif, ); // Encrypt the notification and send - let packet = match session - .encrypt_notification::

(self.node_id, &relay_msg_notif.rlp_encode()) - { - Ok(packet) => packet, - Err(e) => { - return Err(HolePunchError::RelayError(e)); - } - }; + let packet = + match session.encrypt_notification::

(self.node_id, &relay_msg_notif.encode()) { + Ok(packet) => packet, + Err(e) => { + return Err(HolePunchError::Relay(e)); + } + }; self.send(tgt_node_address, packet).await; Ok(()) } else { @@ -1389,9 +1399,7 @@ impl Handler

{ // time out of the udp entrypoint for the target peer in the initiator's NAT, set by // the original timed out FINDNODE request from the initiator, as the initiator may // also be behind a NAT. - Err(HolePunchError::RelayError( - Discv5Error::SessionNotEstablished, - )) + Err(HolePunchError::Relay(Discv5Error::SessionNotEstablished)) } } @@ -1416,39 +1424,33 @@ impl Handler

{ #[async_trait] impl NatHolePunch for Handler

{ - type SessionIndex = NodeAddress; - type Discv5Error = Discv5Error; async fn on_request_time_out( &mut self, - relay: Self::SessionIndex, + relay: NodeAddress, local_enr: Enr, - timed_out_message_nonce: MessageNonce, - target_session_index: Self::SessionIndex, - ) -> Result<(), HolePunchError> { + timed_out_nonce: MessageNonce, + target_session_index: NodeAddress, + ) -> Result<(), HolePunchError> { // Another hole punch process with this target may have just completed. if self.sessions.cache.get(&target_session_index).is_some() { return Ok(()); } if let Some(session) = self.sessions.cache.get_mut(&relay) { - let relay_init_notif = RelayInit( - local_enr, - target_session_index.node_id, - timed_out_message_nonce, - ); + let relay_init_notif = + Notification::RelayInit(local_enr, target_session_index.node_id, timed_out_nonce); trace!( "Sending notif to relay {}. relay init: {}", relay.node_id, relay_init_notif, ); // Encrypt the message and send - let packet = match session - .encrypt_notification::

(self.node_id, &relay_init_notif.rlp_encode()) - { - Ok(packet) => packet, - Err(e) => { - return Err(HolePunchError::RelayError(e)); - } - }; + let packet = + match session.encrypt_notification::

(self.node_id, &relay_init_notif.encode()) { + Ok(packet) => packet, + Err(e) => { + return Err(HolePunchError::Initiator(e)); + } + }; self.send(relay, packet).await; } else { // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays @@ -1465,11 +1467,12 @@ impl NatHolePunch for Handler

{ async fn on_relay_init( &mut self, - notif: RelayInit, - ) -> Result<(), HolePunchError> { - let RelayInit(initiator, tgt, nonce) = notif; + initr: Enr, + tgt: NodeId, + timed_out_nonce: MessageNonce, + ) -> Result<(), HolePunchError> { // Assemble the notification for the target - let relay_msg_notif = RelayMsg(initiator, nonce); + let relay_msg_notif = Notification::RelayMsg(initr, timed_out_nonce); // Check for target peer in our kbuckets otherwise drop notification. if let Err(e) = self @@ -1477,21 +1480,20 @@ impl NatHolePunch for Handler

{ .send(HandlerOut::FindHolePunchEnr(tgt, relay_msg_notif)) .await { - return Err(HolePunchError::RelayError(e.into())); + return Err(HolePunchError::Relay(e.into())); } Ok(()) } async fn on_relay_msg( &mut self, - notif: RelayMsg, - ) -> Result<(), HolePunchError> { - let RelayMsg(initiator, nonce) = notif; - + initr: Enr, + timed_out_nonce: MessageNonce, + ) -> Result<(), HolePunchError> { let initiator_node_address = - match NodeContact::try_from_enr(initiator, self.nat_hole_puncher.ip_mode) { + match NodeContact::try_from_enr(initr, self.nat_hole_puncher.ip_mode) { Ok(contact) => contact.node_address(), - Err(e) => return Err(HolePunchError::TargetError(e.into())), + Err(e) => return Err(HolePunchError::Target(e.into())), }; // A session may already have been established. @@ -1516,22 +1518,18 @@ impl NatHolePunch for Handler

{ } // If not hole punch attempts are in progress, spawn a WHOAREYOU event to punch a hole in // our NAT for initiator. - let whoareyou_ref = WhoAreYouRef(initiator_node_address, nonce); + let whoareyou_ref = WhoAreYouRef(initiator_node_address, timed_out_nonce); if let Err(e) = self .service_send .send(HandlerOut::WhoAreYou(whoareyou_ref)) .await { - return Err(HolePunchError::TargetError(e.into())); + return Err(HolePunchError::Target(e.into())); } Ok(()) } - async fn on_hole_punch_expired( - &mut self, - dst: SocketAddr, - ) -> Result<(), HolePunchError> { - self.send_outbound(dst, Outbound::KeepHolePunched(dst)) - .await; + async fn on_hole_punch_expired(&mut self, dst: SocketAddr) -> Result<(), HolePunchError> { + self.send_outbound(dst.into()).await; Ok(()) } } diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index 8b676377f..6d09bfb36 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -1,18 +1,69 @@ -use crate::{node_info::NodeAddress, Enr, IpMode}; +use crate::{node_info::NodeAddress, packet::MessageNonce, Discv5Error, Enr, IpMode}; +use async_trait::async_trait; use delay_map::HashSetDelay; use enr::NodeId; use futures::{Stream, StreamExt}; -use nat_hole_punch::DEFAULT_HOLE_PUNCH_LIFETIME; +use rand::Rng; use std::{ collections::HashMap, - net::{IpAddr, SocketAddr}, + fmt::Debug, + net::{IpAddr, SocketAddr, UdpSocket}, + ops::RangeInclusive, pin::Pin, task::{Context, Poll}, time::Duration, }; +use thiserror::Error; -/// Types necessary implement trait [`nat_hole_punch::NatHolePunch`] on -/// [`crate::handler::Handler`]. +/// An error occurred whilst attempting to hole punch NAT. +#[derive(Debug, Error)] +pub enum Error { + #[error("NAT error, failed as initiator a hole punch attempt, {0}")] + Initiator(Discv5Error), + #[error("NAT error, failed as relay of a hole punch attempt, {0}")] + Relay(Discv5Error), + #[error("NAT error, failed as target of a hole punch attempt, {0}")] + Target(Discv5Error), +} + +/// The expected shortest lifetime in most NAT configurations of a punched hole in seconds. +pub const DEFAULT_HOLE_PUNCH_LIFETIME: u64 = 20; +/// The default number of ports to try before concluding that the local node is behind NAT. +pub const DEFAULT_PORT_BIND_TRIES: usize = 4; +/// Port range that is not impossible to bind to. +pub const USER_AND_DYNAMIC_PORTS: RangeInclusive = 1025..=u16::MAX; + +#[async_trait] +pub trait NatHolePunch { + /// A request times out. Should trigger the initiation of a hole punch attempt, given a + /// transitive route to the target exists. + async fn on_request_time_out( + &mut self, + relay: NodeAddress, + local_enr: Enr, // initiator-enr + timed_out_nonce: MessageNonce, + target_session_index: NodeAddress, + ) -> Result<(), Error>; + /// A RelayInit notification is received over discv5 indicating this node is the relay. Should + /// trigger sending a RelayMsg to the target. + async fn on_relay_init( + &mut self, + initr: Enr, + tgt: NodeId, + timed_out_nonce: MessageNonce, + ) -> Result<(), Error>; + /// A RelayMsg notification is received over discv5 indicating this node is the target. Should + /// trigger a WHOAREYOU to be sent to the initiator using the `nonce` in the RelayMsg. + async fn on_relay_msg( + &mut self, + initr: Enr, + timed_out_nonce: MessageNonce, + ) -> Result<(), Error>; + /// A punched hole closes. Should trigger an empty packet to be sent to the peer. + async fn on_hole_punch_expired(&mut self, dst: SocketAddr) -> Result<(), Error>; +} + +/// Types necessary implement trait [`NatHolePunch`] on [`super::Handler`]. pub(crate) struct NatHolePuncher { /// Ip mode as set in config. pub ip_mode: IpMode, @@ -35,7 +86,8 @@ impl NatHolePuncher { new_peer_latest_relay: Default::default(), hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), }; - // Optimistically only test one advertised socket, ipv4 has precedence. + // Optimistically only test one advertised socket, ipv4 has precedence. If it is + // reachable, assumption is made that also the other ip version socket is reachable. match ( local_enr.ip4(), local_enr.udp4(), @@ -66,8 +118,8 @@ impl NatHolePuncher { self.hole_punch_tracker.insert(peer_socket); } - // Called when a new observed address is reported at start up or after a - // `Discv5Event::SocketUpdated(socket)` + /// Called when a new observed address is reported at start up or after a + /// [`crate::Discv5Event::SocketUpdated`]. pub(crate) fn set_is_behind_nat( &mut self, listen_port: u16, @@ -83,7 +135,7 @@ impl NatHolePuncher { let Some(ip) = observed_ip else { return; }; - self.is_behind_nat = Some(nat_hole_punch::is_behind_nat(ip, None, None)); + self.is_behind_nat = Some(is_behind_nat(ip, None, None)); } } @@ -99,3 +151,31 @@ impl Stream for NatHolePuncher { self.hole_punch_tracker.poll_next_unpin(cx) } } + +/// Helper function to test if the local node is behind NAT based on the node's observed reachable +/// socket. +pub fn is_behind_nat( + observed_ip: IpAddr, + unused_port_range: Option>, + max_retries: Option, +) -> bool { + // If the node cannot bind to the observed address at any of some random ports, we + // conclude it is behind NAT. + let mut rng = rand::thread_rng(); + let unused_port_range = match unused_port_range { + Some(range) => range, + None => USER_AND_DYNAMIC_PORTS, + }; + let retries = match max_retries { + Some(max) => max, + None => DEFAULT_PORT_BIND_TRIES, + }; + for _ in 0..retries { + let rnd_port: u16 = rng.gen_range(unused_port_range.clone()); + let socket_addr: SocketAddr = format!("{}:{}", observed_ip, rnd_port).parse().unwrap(); + if UdpSocket::bind(socket_addr).is_ok() { + return false; + } + } + true +} diff --git a/src/lib.rs b/src/lib.rs index 72d1e5308..6d316f0e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,6 +110,7 @@ pub mod handler; mod ipmode; pub mod kbucket; mod lru_time_cache; +mod macro_utils; pub mod metrics; mod node_info; pub mod packet; diff --git a/src/macro_utils.rs b/src/macro_utils.rs new file mode 100644 index 000000000..47fb49b39 --- /dev/null +++ b/src/macro_utils.rs @@ -0,0 +1,11 @@ +/// Implements From for an enum from some type. Takes the type to nest, the enum and the variant. +#[macro_export] +macro_rules! impl_from_variant_wrap { + ($from_type: ty, $to_type: ty, $variant: path) => { + impl From<$from_type> for $to_type { + fn from(t: $from_type) -> Self { + $variant(t) + } + } + }; +} diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 8a8f9ee13..777951dce 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -1,3 +1,4 @@ +use crate::impl_from_variant_wrap; use parse_display_derive::Display; use rlp::{DecoderError, Rlp}; @@ -54,16 +55,6 @@ pub enum Message { Notification(Notification), } -macro_rules! impl_from_variant_wrap { - ($from_type: ty, $to_type: ty, $variant: path) => { - impl From<$from_type> for $to_type { - fn from(t: $from_type) -> Self { - $variant(t) - } - } - }; -} - impl_from_variant_wrap!(Request, Message, Self::Request); impl_from_variant_wrap!(Response, Message, Self::Response); impl_from_variant_wrap!(Notification, Message, Self::Notification); diff --git a/src/socket/send.rs b/src/socket/send.rs index 498174327..9c70c9999 100644 --- a/src/socket/send.rs +++ b/src/socket/send.rs @@ -1,6 +1,7 @@ //! This is a standalone task that encodes and sends Discv5 UDP packets -use crate::{metrics::METRICS, node_info::NodeAddress, packet::*, Executor}; -use nat_hole_punch::impl_from_variant_wrap; +use crate::{ + impl_from_variant_wrap, metrics::METRICS, node_info::NodeAddress, packet::*, Executor, +}; use std::{net::SocketAddr, sync::Arc}; use tokio::{ net::UdpSocket, @@ -13,7 +14,17 @@ pub enum Outbound { KeepHolePunched(SocketAddr), } -impl_from_variant_wrap!(, OutboundPacket, Outbound, Self::Packet); +impl Outbound { + pub fn dst(&self) -> &SocketAddr { + match self { + Self::Packet(packet) => &packet.node_address.socket_addr, + Self::KeepHolePunched(dst) => dst, + } + } +} + +impl_from_variant_wrap!(OutboundPacket, Outbound, Self::Packet); +impl_from_variant_wrap!(SocketAddr, Outbound, Self::KeepHolePunched); pub struct OutboundPacket { /// The destination node address From e8bd06732782288025740b55fd2076bca38ba070 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 30 Apr 2023 15:04:08 +0200 Subject: [PATCH 047/154] fixup! Move hole punch trait from nat_hole_punch crate into crate --- src/rpc/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 777951dce..453234e01 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -94,8 +94,7 @@ impl Message { #[cfg(test)] mod tests { use super::*; - use enr::EnrBuilder; - use enr::{CombinedKey, Enr}; + use enr::{CombinedKey, Enr, EnrBuilder}; use std::net::IpAddr; #[test] From e75892f4f049edd7a46881ff69584bc1bd811a6a Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 1 May 2023 14:08:16 +0200 Subject: [PATCH 048/154] Make log message more human readbable --- src/handler/mod.rs | 6 +----- src/node_info.rs | 10 +++------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 2be6aaf96..d5f9393e5 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -477,11 +477,7 @@ impl Handler

{ // The request might be timing out because the peer is behind a NAT. If we // have a relay to the peer, attempt NAT hole punching. let target = request_call.contact().node_address(); - trace!( - "Trying to hole punch target {} with relay {:?}", - target, - relay - ); + trace!("Trying to hole punch target {target} with relay {relay}"); let local_enr = self.enr.read().clone(); let nonce = request_call.packet().header.message_nonce; match self diff --git a/src/node_info.rs b/src/node_info.rs index 7d5b8014f..4e6d39ded 100644 --- a/src/node_info.rs +++ b/src/node_info.rs @@ -1,6 +1,7 @@ use super::*; use crate::Enr; use enr::{CombinedPublicKey, NodeId}; +use parse_display_derive::Display; use std::net::SocketAddr; #[cfg(feature = "libp2p")] @@ -146,7 +147,8 @@ impl std::fmt::Display for NodeContact { } /// A representation of an unsigned contactable node. -#[derive(PartialEq, Hash, Eq, Clone, Debug)] +#[derive(PartialEq, Hash, Eq, Clone, Debug, Display)] +#[display("Node: {node_id}, addr: {socket_addr}")] pub struct NodeAddress { /// The destination socket address. pub socket_addr: SocketAddr, @@ -182,9 +184,3 @@ impl NodeAddress { } } } - -impl std::fmt::Display for NodeAddress { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Node: {}, addr: {:?}", self.node_id, self.socket_addr) - } -} From dc772d542c4b19e394b1c6ecdb1ddcd34d9f0bf0 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 3 May 2023 11:39:40 +0200 Subject: [PATCH 049/154] Clean up --- src/handler/mod.rs | 6 +++--- src/handler/nat_hole_puncher.rs | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index d5f9393e5..2cf6f55e7 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -67,7 +67,7 @@ use crate::metrics::METRICS; pub use crate::node_info::{NodeAddress, NodeContact}; use active_requests::ActiveRequests; -use nat_hole_puncher::{Error as HolePunchError, NatHolePunch, NatHolePuncher}; +use nat_hole_puncher::{Error as HolePunchError, NatHolePunch, NatHolePunchUtils}; use request_call::RequestCall; use sessions::{Session, Sessions}; @@ -219,7 +219,7 @@ pub struct Handler { /// Access generic when implementing traits for Handler. _phantom: PhantomData

, /// Types necessary to plug in nat hole punching. - nat_hole_puncher: NatHolePuncher, + nat_hole_puncher: NatHolePunchUtils, } type HandlerReturn = ( @@ -272,7 +272,7 @@ impl Handler

{ let socket = Socket::new::

(socket_config).await?; let nat_hole_puncher = - NatHolePuncher::new(listen_socket.port(), &enr.read(), config.ip_mode); + NatHolePunchUtils::new(listen_socket.port(), &enr.read(), config.ip_mode); let sessions = Sessions::new( config.session_cache_capacity, diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index 6d09bfb36..b2b21f824 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -15,6 +15,13 @@ use std::{ }; use thiserror::Error; +/// The expected shortest lifetime in most NAT configurations of a punched hole in seconds. +pub const DEFAULT_HOLE_PUNCH_LIFETIME: u64 = 20; +/// The default number of ports to try before concluding that the local node is behind NAT. +pub const DEFAULT_PORT_BIND_TRIES: usize = 4; +/// Port range that is not impossible to bind to. +pub const USER_AND_DYNAMIC_PORTS: RangeInclusive = 1025..=u16::MAX; + /// An error occurred whilst attempting to hole punch NAT. #[derive(Debug, Error)] pub enum Error { @@ -26,13 +33,6 @@ pub enum Error { Target(Discv5Error), } -/// The expected shortest lifetime in most NAT configurations of a punched hole in seconds. -pub const DEFAULT_HOLE_PUNCH_LIFETIME: u64 = 20; -/// The default number of ports to try before concluding that the local node is behind NAT. -pub const DEFAULT_PORT_BIND_TRIES: usize = 4; -/// Port range that is not impossible to bind to. -pub const USER_AND_DYNAMIC_PORTS: RangeInclusive = 1025..=u16::MAX; - #[async_trait] pub trait NatHolePunch { /// A request times out. Should trigger the initiation of a hole punch attempt, given a @@ -64,7 +64,7 @@ pub trait NatHolePunch { } /// Types necessary implement trait [`NatHolePunch`] on [`super::Handler`]. -pub(crate) struct NatHolePuncher { +pub(crate) struct NatHolePunchUtils { /// Ip mode as set in config. pub ip_mode: IpMode, /// This node has been observed to be behind a NAT. @@ -78,9 +78,9 @@ pub(crate) struct NatHolePuncher { pub hole_punch_tracker: HashSetDelay, } -impl NatHolePuncher { +impl NatHolePunchUtils { pub(crate) fn new(listen_port: u16, local_enr: &Enr, ip_mode: IpMode) -> Self { - let mut nat_hole_puncher = NatHolePuncher { + let mut nat_hole_puncher = NatHolePunchUtils { ip_mode, is_behind_nat: None, new_peer_latest_relay: Default::default(), @@ -139,7 +139,7 @@ impl NatHolePuncher { } } -impl Stream for NatHolePuncher { +impl Stream for NatHolePunchUtils { type Item = Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { From 42714f38465676e7faa103e611dd93365083400b Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 3 May 2023 13:04:49 +0200 Subject: [PATCH 050/154] Stop punching holes for disconnected peers --- src/handler/mod.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 2cf6f55e7..358ca120d 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -340,11 +340,12 @@ impl Handler

{ let port = socket.port(); if old_socket.is_none() { // This node goes from being unreachable to being reachable. Remove - // its sessions to trigger a WHOAREYOU from peers. If the peer is - // running this implementation of discovery, this makes it possible - // for the local node to be inserted into its peers' kbuckets - // before the session they already had expires. Session duration, - // in this impl defaults to 24 hours. + // its sessions to trigger a WHOAREYOU from peers on next sent + // message. If the peer is running this implementation of + // discovery, this makes it possible for the local node to be + // inserted into its peers' kbuckets before the session they + // already had expires. Session duration, in this impl defaults to + // 24 hours. self.sessions.cache.clear() } self.nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip), Some(port)); @@ -1309,6 +1310,10 @@ impl Handler

{ METRICS .active_sessions .store(self.sessions.cache.len(), Ordering::Relaxed); + // stop keeping hole punched for peer + self.nat_hole_puncher + .hole_punch_tracker + .remove(&node_address.socket_addr); } if let Some(to_remove) = self.pending_requests.remove(node_address) { for PendingRequest { request_id, .. } in to_remove { From a005ce71d16d2c68ddf976eb08d294a3fd14e2a3 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 3 May 2023 17:08:19 +0200 Subject: [PATCH 051/154] Fix session limiter constructor bug to not write to logs on no limit and add limiter test --- src/config.rs | 2 +- src/handler/sessions/mod.rs | 91 +++++++++++++++++++++++++++++++-- src/handler/sessions/session.rs | 9 ++++ 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/src/config.rs b/src/config.rs index 19b7d2204..9e69d3a00 100644 --- a/src/config.rs +++ b/src/config.rs @@ -29,7 +29,7 @@ pub struct Discv5Config { /// The number of retries for each UDP request. Default: 1. pub request_retries: u8, - /// The session timeout for each node. Default: 1 day. + /// The session timeout for each node in seconds. Default: 1 day. pub session_timeout: Duration, /// The maximum number of established sessions to maintain. Default: 1000. diff --git a/src/handler/sessions/mod.rs b/src/handler/sessions/mod.rs index 097140552..124d718e0 100644 --- a/src/handler/sessions/mod.rs +++ b/src/handler/sessions/mod.rs @@ -19,12 +19,97 @@ impl Sessions { entry_ttl: Duration, unreachable_enr_limit: Option, ) -> Self { - let (tx, rx) = futures::channel::mpsc::channel::(cache_capacity); - let sessions = LruTimeCache::new(entry_ttl, Some(cache_capacity), Some(tx)); - let limiter = unreachable_enr_limit.map(|limit| SessionLimiter::new(rx, limit)); + let (tx, limiter) = match unreachable_enr_limit { + Some(limit) => { + let (tx, rx) = futures::channel::mpsc::channel::(cache_capacity); + let limiter = SessionLimiter::new(rx, limit); + (Some(tx), Some(limiter)) + } + None => (None, None), + }; + let sessions = LruTimeCache::new(entry_ttl, Some(cache_capacity), tx); Sessions { cache: sessions, limiter, } } } + +#[cfg(test)] +mod test { + use super::*; + use enr::{CombinedKey, EnrBuilder}; + + #[tokio::test] + async fn test_limiter() { + let max_nodes_unreachable_enr = 2; + let session_time_out = Duration::from_secs(10); + let mut sessions = Sessions::new(3, session_time_out, Some(max_nodes_unreachable_enr)); + + // first node + let first_key = CombinedKey::generate_secp256k1(); + let mut builder = EnrBuilder::new("v4"); + let first_enr = builder.build(&first_key).unwrap(); + + let first_unreachable_node = NodeAddress { + socket_addr: "0.0.0.0:10010".parse().unwrap(), + node_id: first_enr.node_id(), + }; + // second node + let second_key = CombinedKey::generate_secp256k1(); + builder = EnrBuilder::new("v4"); + let second_enr = builder.build(&second_key).unwrap(); + + let second_unreachable_node = NodeAddress { + socket_addr: "0.0.0.0:10011".parse().unwrap(), + node_id: second_enr.node_id(), + }; + // third node + let third_key = CombinedKey::generate_secp256k1(); + builder = EnrBuilder::new("v4"); + let third_enr = builder.build(&third_key).unwrap(); + + let third_unreachable_node = NodeAddress { + socket_addr: "0.0.0.0:10012".parse().unwrap(), + node_id: third_enr.node_id(), + }; + // check if space for first node + let res = sessions.limiter.as_mut().map(|limiter| { + limiter.track_sessions_unreachable_enr(&first_unreachable_node, &first_enr) + }); + res.unwrap() + .expect("should be space for first unreachable node"); + // insert first node + let first_session = Session::new(([0u8; 16], [0u8; 16]).into()); + sessions.cache.insert(first_unreachable_node, first_session); + // check if space for second node + let second_res = sessions.limiter.as_mut().map(|limiter| { + limiter.track_sessions_unreachable_enr(&second_unreachable_node, &second_enr) + }); + second_res + .unwrap() + .expect("should be space for second unreachable node"); + // insert second node + let second_session = Session::new(([0u8; 16], [0u8; 16]).into()); + sessions + .cache + .insert(second_unreachable_node, second_session); + + // check if space for third node, should fail + let third_res = sessions.limiter.as_mut().map(|limiter| { + limiter.track_sessions_unreachable_enr(&third_unreachable_node, &third_enr) + }); + assert!(third_res.unwrap().is_err()); + // let sessions expire + tokio::time::sleep(session_time_out).await; + // calling `len` removes expired entries + sessions.cache.len(); + // retry check if space for third node, should be successful + let third_res = sessions.limiter.as_mut().map(|limiter| { + limiter.track_sessions_unreachable_enr(&third_unreachable_node, &third_enr) + }); + third_res + .unwrap() + .expect("should be space for third unreachable node"); + } +} diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index 781ad9544..30c73bd27 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -24,6 +24,15 @@ pub struct Keys { decryption_key: [u8; 16], } +impl From<([u8; 16], [u8; 16])> for Keys { + fn from((encryption_key, decryption_key): ([u8; 16], [u8; 16])) -> Self { + Keys { + encryption_key, + decryption_key, + } + } +} + /// A Session containing the encryption/decryption keys. These are kept individually for a given /// node. pub struct Session { From 1031f37edd5b7bbb4bb685820e3c99800f484552 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 3 May 2023 17:44:25 +0200 Subject: [PATCH 052/154] Fix wrong comments on message types and collect message types --- src/error.rs | 2 ++ src/rpc/mod.rs | 55 +++++++++++++++++++++++++---------------- src/rpc/notification.rs | 13 +++++----- src/rpc/request.rs | 17 +++++++------ src/rpc/response.rs | 21 +++++++++------- 5 files changed, 64 insertions(+), 44 deletions(-) diff --git a/src/error.rs b/src/error.rs index eac649179..76cea6ae8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,6 +5,8 @@ use std::fmt; #[derive(Debug)] /// A general error that is used throughout the Discv5 library. pub enum Discv5Error { + /// An invalid message type was received. + InvalidMessage, /// An invalid ENR was received. InvalidEnr, /// An invalid ENR was received. diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 453234e01..ef1c90753 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -1,6 +1,7 @@ use crate::impl_from_variant_wrap; use parse_display_derive::Display; use rlp::{DecoderError, Rlp}; +use std::convert::{TryFrom, TryInto}; mod notification; mod request; @@ -11,22 +12,35 @@ pub use request::{Request, RequestBody, RequestId}; pub use response::{Response, ResponseBody}; /// Message type IDs. -/// Ping notification type. -pub const PING_MSG_TYPE: u8 = 1; -/// Pong notification type. -pub const PONG_MSG_TYPE: u8 = 2; -/// FindNode notification type. -pub const FINDNODE_MSG_TYPE: u8 = 3; -/// Nodes notification type. -pub const NODES_MSG_TYPE: u8 = 4; -/// TalkReq notification type. -pub const TALKREQ_MSG_TYPE: u8 = 5; -/// TalkResp notification type. -pub const TALKRESP_MSG_TYPE: u8 = 6; -/// RelayInit notification type. -pub const RELAYINIT_MSG_TYPE: u8 = 7; -/// RelayMsg notification type. -pub const RELAYMSG_MSG_TYPE: u8 = 8; +#[derive(Debug)] +#[repr(u8)] +pub enum MessageType { + Ping = 1, + Pong = 2, + FindNode = 3, + Nodes = 4, + TalkReq = 5, + TalkResp = 6, + RelayInit = 7, + RelayMsg = 8, +} + +impl TryFrom for MessageType { + type Error = DecoderError; + fn try_from(byte: u8) -> Result { + match byte { + 1 => Ok(MessageType::Ping), + 2 => Ok(MessageType::Pong), + 3 => Ok(MessageType::FindNode), + 4 => Ok(MessageType::Nodes), + 5 => Ok(MessageType::TalkReq), + 6 => Ok(MessageType::TalkResp), + 7 => Ok(MessageType::RelayInit), + 8 => Ok(MessageType::RelayMsg), + _ => Err(DecoderError::Custom("Unknown RPC message type")), + } + } +} /// The payload of message containers SessionMessage, Message or Handshake type. pub trait Payload @@ -76,17 +90,16 @@ impl Message { let msg_type = data[0]; let rlp = rlp::Rlp::new(&data[1..]); - match msg_type { - PING_MSG_TYPE | FINDNODE_MSG_TYPE | TALKREQ_MSG_TYPE => { + match msg_type.try_into()? { + MessageType::Ping | MessageType::FindNode | MessageType::TalkReq => { Ok(Request::decode(msg_type, &rlp)?.into()) } - PONG_MSG_TYPE | NODES_MSG_TYPE | TALKRESP_MSG_TYPE => { + MessageType::Pong | MessageType::Nodes | MessageType::TalkResp => { Ok(Response::decode(msg_type, &rlp)?.into()) } - RELAYINIT_MSG_TYPE | RELAYMSG_MSG_TYPE => { + MessageType::RelayInit | MessageType::RelayMsg => { Ok(Notification::decode(msg_type, &rlp)?.into()) } - _ => Err(DecoderError::Custom("Unknown RPC message type")), } } } diff --git a/src/rpc/notification.rs b/src/rpc/notification.rs index 22c86614b..3be282022 100644 --- a/src/rpc/notification.rs +++ b/src/rpc/notification.rs @@ -1,4 +1,4 @@ -use super::{Payload, RELAYINIT_MSG_TYPE, RELAYMSG_MSG_TYPE}; +use super::{MessageType, Payload}; use crate::{ packet::{MessageNonce, MESSAGE_NONCE_LENGTH}, Enr, @@ -6,6 +6,7 @@ use crate::{ use enr::NodeId; use parse_display_derive::Display; use rlp::{DecoderError, Rlp, RlpStream}; +use std::convert::TryInto; /// Nonce of request that triggered the initiation of this hole punching attempt. type NonceOfTimedOutMessage = MessageNonce; @@ -27,8 +28,8 @@ impl Payload for Notification { /// Matches a notification type to its message type id. fn msg_type(&self) -> u8 { match self { - Self::RelayInit(..) => RELAYINIT_MSG_TYPE, - Self::RelayMsg(..) => RELAYMSG_MSG_TYPE, + Self::RelayInit(..) => MessageType::RelayInit as u8, + Self::RelayMsg(..) => MessageType::RelayMsg as u8, } } @@ -57,8 +58,8 @@ impl Payload for Notification { /// Decodes RLP-encoded bytes into a notification message. fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { - match msg_type { - RELAYINIT_MSG_TYPE => { + match msg_type.try_into()? { + MessageType::RelayInit => { if rlp.item_count()? != 3 { return Err(DecoderError::RlpIncorrectListLen); } @@ -84,7 +85,7 @@ impl Payload for Notification { Ok(Notification::RelayInit(initiator, tgt, nonce)) } - RELAYMSG_MSG_TYPE => { + MessageType::RelayMsg => { if rlp.item_count()? != 2 { return Err(DecoderError::RlpIncorrectListLen); } diff --git a/src/rpc/request.rs b/src/rpc/request.rs index 78516e994..1ee1b45ba 100644 --- a/src/rpc/request.rs +++ b/src/rpc/request.rs @@ -1,6 +1,7 @@ -use super::{Payload, FINDNODE_MSG_TYPE, PING_MSG_TYPE, TALKREQ_MSG_TYPE}; +use super::{MessageType, Payload}; use parse_display_derive::Display; use rlp::{DecoderError, Rlp, RlpStream}; +use std::convert::TryInto; use tracing::{debug, warn}; /// A request sent between nodes. @@ -17,9 +18,9 @@ impl Payload for Request { /// Matches a request type to its message type id. fn msg_type(&self) -> u8 { match self.body { - RequestBody::Ping { .. } => PING_MSG_TYPE, - RequestBody::FindNode { .. } => FINDNODE_MSG_TYPE, - RequestBody::TalkReq { .. } => TALKREQ_MSG_TYPE, + RequestBody::Ping { .. } => MessageType::Ping as u8, + RequestBody::FindNode { .. } => MessageType::FindNode as u8, + RequestBody::TalkReq { .. } => MessageType::TalkReq as u8, } } @@ -65,8 +66,8 @@ impl Payload for Request { fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { let list_len = rlp.item_count()?; let id = RequestId::decode(rlp.val_at::>(0)?)?; - let message = match msg_type { - PING_MSG_TYPE => { + let message = match msg_type.try_into()? { + MessageType::Ping => { // Ping Request if list_len != 2 { debug!( @@ -82,7 +83,7 @@ impl Payload for Request { }, } } - FINDNODE_MSG_TYPE => { + MessageType::FindNode => { // FindNode Request if list_len != 2 { debug!( @@ -108,7 +109,7 @@ impl Payload for Request { body: RequestBody::FindNode { distances }, } } - TALKREQ_MSG_TYPE => { + MessageType::TalkReq => { // Talk Request if list_len != 3 { debug!( diff --git a/src/rpc/response.rs b/src/rpc/response.rs index de9093bbe..0025b0e92 100644 --- a/src/rpc/response.rs +++ b/src/rpc/response.rs @@ -1,8 +1,11 @@ -use super::{Payload, RequestBody, RequestId, NODES_MSG_TYPE, PONG_MSG_TYPE, TALKRESP_MSG_TYPE}; +use super::{MessageType, Payload, RequestBody, RequestId}; use crate::Enr; use parse_display_derive::Display; use rlp::{DecoderError, Rlp, RlpStream}; -use std::net::{IpAddr, Ipv6Addr}; +use std::{ + convert::TryInto, + net::{IpAddr, Ipv6Addr}, +}; use tracing::debug; /// A response sent in response to a [`super::Request`] @@ -19,9 +22,9 @@ impl Payload for Response { /// Matches a response type to its message type id. fn msg_type(&self) -> u8 { match &self.body { - ResponseBody::Pong { .. } => PONG_MSG_TYPE, - ResponseBody::Nodes { .. } => NODES_MSG_TYPE, - ResponseBody::TalkResp { .. } => TALKRESP_MSG_TYPE, + ResponseBody::Pong { .. } => MessageType::Pong as u8, + ResponseBody::Nodes { .. } => MessageType::Nodes as u8, + ResponseBody::TalkResp { .. } => MessageType::TalkResp as u8, } } @@ -77,8 +80,8 @@ impl Payload for Response { fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { let list_len = rlp.item_count()?; let id = RequestId::decode(rlp.val_at::>(0)?)?; - let response = match msg_type { - PONG_MSG_TYPE => { + let response = match msg_type.try_into()? { + MessageType::Pong => { // Pong Response if list_len != 4 { debug!( @@ -120,7 +123,7 @@ impl Payload for Response { }, } } - NODES_MSG_TYPE => { + MessageType::Nodes => { // Nodes Response if list_len != 3 { debug!( @@ -147,7 +150,7 @@ impl Payload for Response { }, } } - TALKRESP_MSG_TYPE => { + MessageType::TalkResp => { // Talk Response if list_len != 2 { debug!( From 6ee4f7f975e5ebd4a19d98dbdfdb680e4bfc6a1d Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Sun, 14 Jun 2020 10:59:50 +1000 Subject: [PATCH 053/154] Check for unused deps in CI (#1262) * Check for unused deps in CI * Bump slashing protection parking_lot version --- .github/workflows/test-suite.yml | 134 ++ Cargo.lock | 2359 ++++++++++++++++++++++++++++++ Makefile | 83 ++ 3 files changed, 2576 insertions(+) create mode 100644 .github/workflows/test-suite.yml create mode 100644 Cargo.lock create mode 100644 Makefile diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml new file mode 100644 index 000000000..34554afaa --- /dev/null +++ b/.github/workflows/test-suite.yml @@ -0,0 +1,134 @@ +name: test-suite + +on: + push: + branches: + - master + pull_request: + +jobs: + cargo-fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Get latest version of stable Rust + run: rustup update stable + - name: Check formatting with cargo fmt + run: make cargo-fmt + release-tests-ubuntu: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Get latest version of stable Rust + run: rustup update stable + - name: Install ganache-cli + run: sudo npm install -g ganache-cli + - name: Run tests in release + run: make test-release + release-tests-and-install-macos: + runs-on: macos-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Get latest version of stable Rust + run: rustup update stable + - name: Install ganache-cli + run: sudo npm install -g ganache-cli + - name: Run tests in release + run: make test-release + - name: Install Lighthouse + run: make + debug-tests-ubuntu: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Get latest version of stable Rust + run: rustup update stable + - name: Install ganache-cli + run: sudo npm install -g ganache-cli + - name: Run tests in debug + run: make test-debug + state-transition-vectors-ubuntu: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Get latest version of stable Rust + run: rustup update stable + - name: Run state_transition_vectors in release. + run: make run-state-transition-tests + ef-tests-ubuntu: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Get latest version of stable Rust + run: rustup update stable + - name: Run eth2.0-spec-tests with and without fake_crypto + run: make test-ef + dockerfile-ubuntu: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Build the root Dockerfile + run: docker build . + eth1-simulator-ubuntu: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Install ganache-cli + run: sudo npm install -g ganache-cli + - name: Run the beacon chain sim that starts from an eth1 contract + run: cargo run --release --bin simulator eth1-sim + no-eth1-simulator-ubuntu: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Install ganache-cli + run: sudo npm install -g ganache-cli + - name: Run the beacon chain sim without an eth1 connection + run: cargo run --release --bin simulator no-eth1-sim + check-benchmarks: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Typecheck benchmark code without running it + run: make check-benches + clippy: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Lint code for quality and style with Clippy + run: make lint + arbitrary-check: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Validate state_processing feature arbitrary-fuzz + run: make arbitrary-fuzz + cargo-audit: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Run cargo audit to identify known security vulnerabilities reported to the RustSec Advisory Database + run: make audit + cargo-udeps: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Get latest version of nightly Rust + run: rustup update nightly + - name: Install cargo-udeps + run: cargo install cargo-udeps --locked + - name: Run cargo udeps to identify unused crates in the dependency graph + run: make udeps diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..4b4d8e5cf --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2359 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", + "ctr", + "opaque-debug", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.9", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" + +[[package]] +name = "arc-swap" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "asn1_der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "colored" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "data-encoding" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" + +[[package]] +name = "delay_map" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4355c25cbf99edcb6b4a0e906f6bdc6956eda149e84455bea49696429b2f8e8" +dependencies = [ + "futures", + "tokio-util 0.7.8", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "discv5" +version = "0.2.2" +dependencies = [ + "aes", + "aes-gcm", + "arrayvec", + "async-trait", + "clap", + "delay_map", + "enr", + "env_logger 0.9.3", + "fnv", + "futures", + "hashlink", + "hex", + "hex-literal", + "hkdf", + "lazy_static", + "libp2p-core", + "lru", + "mio", + "more-asserts", + "parking_lot 0.11.2", + "parse-display-derive", + "quickcheck", + "rand 0.7.3", + "rand 0.8.5", + "rand_core 0.6.4", + "rand_xorshift", + "rlp", + "simple_logger", + "smallvec", + "socket2", + "thiserror", + "tokio", + "tokio-context", + "tokio-stream", + "tokio-util 0.6.10", + "tracing", + "tracing-subscriber", + "uint", + "zeroize", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enr" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "492a7e5fc2504d5fdce8e124d3e263b244a68b283cac67a69eda0cd43e0aebad" +dependencies = [ + "base64", + "bs58", + "bytes", + "ed25519-dalek", + "hex", + "k256", + "log", + "rand 0.8.5", + "rlp", + "serde", + "sha3", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +dependencies = [ + "hashbrown 0.11.2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "libp2p-core" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1fff5bd889c82a0aec668f2045edd066f559d4e5c40354e5a4c77ac00caac38" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "lazy_static", + "libsecp256k1", + "log", + "multiaddr", + "multihash", + "multistream-select", + "p256", + "parking_lot 0.12.1", + "pin-project", + "prost", + "prost-build", + "rand 0.8.5", + "rw-stream-sink", + "sha2 0.10.6", + "smallvec", + "thiserror", + "unsigned-varint", + "void", + "zeroize", +] + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", +] + +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" + +[[package]] +name = "multiaddr" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" +dependencies = [ + "arrayref", + "bs58", + "byteorder", + "data-encoding", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "multimap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce" + +[[package]] +name = "multistream-select" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "363a84be6453a70e63513660f4894ef815daf88e3356bffcda9ca27d810ce83b" +dependencies = [ + "bytes", + "futures", + "log", + "pin-project", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "os_str_bytes" +version = "6.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.7", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "parse-display-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5587062be441f3d868f7c4c9d13c67f286b03aa679d7f8176ef80bf2ee79e5d" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "regex", + "regex-syntax 0.6.29", + "structmeta", + "syn 1.0.109", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes", + "heck", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 1.0.109", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost", +] + +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +dependencies = [ + "env_logger 0.7.1", + "log", + "rand 0.7.3", + "rand_core 0.5.1", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.14", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.14", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.9", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.1", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + +[[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +dependencies = [ + "winapi", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac 0.12.1", + "zeroize", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rw-stream-sink" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" +dependencies = [ + "futures", + "pin-project", + "static_assertions", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" +dependencies = [ + "arc-swap", + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + +[[package]] +name = "simple_logger" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45b60258a35dc3cb8a16890b8fd6723349bfa458d7960e25e633f1b1c19d7b5e" +dependencies = [ + "atty", + "colored", + "log", + "time", + "winapi", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "structmeta" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104842d6278bf64aa9d2f182ba4bde31e8aec7a131d29b7f444bb9b344a09e2a" +dependencies = [ + "proc-macro2", + "quote", + "structmeta-derive", + "syn 1.0.109", +] + +[[package]] +name = "structmeta-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24420be405b590e2d746d83b01f09af673270cf80e9b003a5fa7b651c58c7d93" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "rand 0.7.3", + "redox_syscall 0.1.56", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +dependencies = [ + "itoa", + "libc", + "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +dependencies = [ + "time-core", +] + +[[package]] +name = "tokio" +version = "1.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-context" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf0b8394dd5ca9a1b726c629390154c19222dfd7467a4b56f1ced90adee3958" +dependencies = [ + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "slab", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "slab", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +dependencies = [ + "smallvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" + +[[package]] +name = "url" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +dependencies = [ + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "which" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c14ef7e1b8b8ecfc75d5eca37949410046e66f15d185c01d70824f1f8111ef" +dependencies = [ + "libc", + "thiserror", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..7f8ea3052 --- /dev/null +++ b/Makefile @@ -0,0 +1,83 @@ +.PHONY: tests + +EF_TESTS = "testing/ef_tests" +STATE_TRANSITION_VECTORS = "testing/state_transition_vectors" + +# Builds the Lighthouse binary in release (optimized). +# +# Binaries will most likely be found in `./target/release` +install: + cargo install --path lighthouse --force --locked + +# Builds the lcli binary in release (optimized). +install-lcli: + cargo install --path lcli --force --locked + +# Runs the full workspace tests in **release**, without downloading any additional +# test vectors. +test-release: + cargo test --all --release --exclude ef_tests + +# Runs the full workspace tests in **debug**, without downloading any additional test +# vectors. +test-debug: + cargo test --all --exclude ef_tests + +# Runs cargo-fmt (linter). +cargo-fmt: + cargo fmt --all -- --check + +# Typechecks benchmark code +check-benches: + cargo check --all --benches + +# Runs only the ef-test vectors. +run-ef-tests: + cargo test --release --manifest-path=$(EF_TESTS)/Cargo.toml --features "ef_tests" + cargo test --release --manifest-path=$(EF_TESTS)/Cargo.toml --features "ef_tests,fake_crypto" + +# Runs only the tests/state_transition_vectors tests. +run-state-transition-tests: + make -C $(STATE_TRANSITION_VECTORS) test + +# Downloads and runs the EF test vectors. +test-ef: make-ef-tests run-ef-tests + +# Runs the full workspace tests in release, without downloading any additional +# test vectors. +test: test-release + +# Runs the entire test suite, downloading test vectors if required. +test-full: cargo-fmt test-release test-debug test-ef + +# Lints the code for bad style and potentially unsafe arithmetic using Clippy. +# Clippy lints are opt-in per-crate for now, which is why we allow all by default. +lint: + cargo clippy --all -- -A clippy::all + +# Runs the makefile in the `ef_tests` repo. +# +# May download and extract an archive of test vectors from the ethereum +# repositories. At the time of writing, this was several hundred MB of +# downloads which extracts into several GB of test vectors. +make-ef-tests: + make -C $(EF_TESTS) + +# Verifies that state_processing feature arbitrary-fuzz will compile +arbitrary-fuzz: + cargo check --manifest-path=consensus/state_processing/Cargo.toml --features arbitrary-fuzz + +# Runs cargo audit (Audit Cargo.lock files for crates with security vulnerabilities reported to the RustSec Advisory Database) +audit: + cargo install --force cargo-audit + cargo audit + +# Runs `cargo udeps` to check for unused dependencies +udeps: + cargo +nightly udeps --tests --all-targets --release + +# Performs a `cargo` clean and cleans the `ef_tests` directory. +clean: + cargo clean + make -C $(EF_TESTS) clean + make -C $(STATE_TRANSITION_VECTORS) clean From b82adc7665df53de66b51e957d478f9bf9002ea9 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 May 2023 08:18:15 +0200 Subject: [PATCH 054/154] Cherry-pick 6ee4f7f9 --- .github/workflows/build.yml | 11 +++ .github/workflows/test-suite.yml | 134 ------------------------------- Makefile | 83 ------------------- 3 files changed, 11 insertions(+), 217 deletions(-) delete mode 100644 .github/workflows/test-suite.yml delete mode 100644 Makefile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9d4b98240..c3205659a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,3 +55,14 @@ jobs: run: rustup update stable - name: Check rustdoc links run: RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --verbose --workspace --no-deps --document-private-items + cargo-udeps: + runs-on: ubuntu-latest + needs: cargo-fmt + steps: + - uses: actions/checkout@v1 + - name: Get latest version of nightly Rust + run: rustup update nightly + - name: Install cargo-udeps + run: cargo install cargo-udeps --locked + - name: Run cargo udeps to identify unused crates in the dependency graph + run: cargo +nightly udeps --tests --all-targets --release diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml deleted file mode 100644 index 34554afaa..000000000 --- a/.github/workflows/test-suite.yml +++ /dev/null @@ -1,134 +0,0 @@ -name: test-suite - -on: - push: - branches: - - master - pull_request: - -jobs: - cargo-fmt: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Get latest version of stable Rust - run: rustup update stable - - name: Check formatting with cargo fmt - run: make cargo-fmt - release-tests-ubuntu: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Get latest version of stable Rust - run: rustup update stable - - name: Install ganache-cli - run: sudo npm install -g ganache-cli - - name: Run tests in release - run: make test-release - release-tests-and-install-macos: - runs-on: macos-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Get latest version of stable Rust - run: rustup update stable - - name: Install ganache-cli - run: sudo npm install -g ganache-cli - - name: Run tests in release - run: make test-release - - name: Install Lighthouse - run: make - debug-tests-ubuntu: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Get latest version of stable Rust - run: rustup update stable - - name: Install ganache-cli - run: sudo npm install -g ganache-cli - - name: Run tests in debug - run: make test-debug - state-transition-vectors-ubuntu: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Get latest version of stable Rust - run: rustup update stable - - name: Run state_transition_vectors in release. - run: make run-state-transition-tests - ef-tests-ubuntu: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Get latest version of stable Rust - run: rustup update stable - - name: Run eth2.0-spec-tests with and without fake_crypto - run: make test-ef - dockerfile-ubuntu: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Build the root Dockerfile - run: docker build . - eth1-simulator-ubuntu: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Install ganache-cli - run: sudo npm install -g ganache-cli - - name: Run the beacon chain sim that starts from an eth1 contract - run: cargo run --release --bin simulator eth1-sim - no-eth1-simulator-ubuntu: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Install ganache-cli - run: sudo npm install -g ganache-cli - - name: Run the beacon chain sim without an eth1 connection - run: cargo run --release --bin simulator no-eth1-sim - check-benchmarks: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Typecheck benchmark code without running it - run: make check-benches - clippy: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Lint code for quality and style with Clippy - run: make lint - arbitrary-check: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Validate state_processing feature arbitrary-fuzz - run: make arbitrary-fuzz - cargo-audit: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Run cargo audit to identify known security vulnerabilities reported to the RustSec Advisory Database - run: make audit - cargo-udeps: - runs-on: ubuntu-latest - needs: cargo-fmt - steps: - - uses: actions/checkout@v1 - - name: Get latest version of nightly Rust - run: rustup update nightly - - name: Install cargo-udeps - run: cargo install cargo-udeps --locked - - name: Run cargo udeps to identify unused crates in the dependency graph - run: make udeps diff --git a/Makefile b/Makefile deleted file mode 100644 index 7f8ea3052..000000000 --- a/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -.PHONY: tests - -EF_TESTS = "testing/ef_tests" -STATE_TRANSITION_VECTORS = "testing/state_transition_vectors" - -# Builds the Lighthouse binary in release (optimized). -# -# Binaries will most likely be found in `./target/release` -install: - cargo install --path lighthouse --force --locked - -# Builds the lcli binary in release (optimized). -install-lcli: - cargo install --path lcli --force --locked - -# Runs the full workspace tests in **release**, without downloading any additional -# test vectors. -test-release: - cargo test --all --release --exclude ef_tests - -# Runs the full workspace tests in **debug**, without downloading any additional test -# vectors. -test-debug: - cargo test --all --exclude ef_tests - -# Runs cargo-fmt (linter). -cargo-fmt: - cargo fmt --all -- --check - -# Typechecks benchmark code -check-benches: - cargo check --all --benches - -# Runs only the ef-test vectors. -run-ef-tests: - cargo test --release --manifest-path=$(EF_TESTS)/Cargo.toml --features "ef_tests" - cargo test --release --manifest-path=$(EF_TESTS)/Cargo.toml --features "ef_tests,fake_crypto" - -# Runs only the tests/state_transition_vectors tests. -run-state-transition-tests: - make -C $(STATE_TRANSITION_VECTORS) test - -# Downloads and runs the EF test vectors. -test-ef: make-ef-tests run-ef-tests - -# Runs the full workspace tests in release, without downloading any additional -# test vectors. -test: test-release - -# Runs the entire test suite, downloading test vectors if required. -test-full: cargo-fmt test-release test-debug test-ef - -# Lints the code for bad style and potentially unsafe arithmetic using Clippy. -# Clippy lints are opt-in per-crate for now, which is why we allow all by default. -lint: - cargo clippy --all -- -A clippy::all - -# Runs the makefile in the `ef_tests` repo. -# -# May download and extract an archive of test vectors from the ethereum -# repositories. At the time of writing, this was several hundred MB of -# downloads which extracts into several GB of test vectors. -make-ef-tests: - make -C $(EF_TESTS) - -# Verifies that state_processing feature arbitrary-fuzz will compile -arbitrary-fuzz: - cargo check --manifest-path=consensus/state_processing/Cargo.toml --features arbitrary-fuzz - -# Runs cargo audit (Audit Cargo.lock files for crates with security vulnerabilities reported to the RustSec Advisory Database) -audit: - cargo install --force cargo-audit - cargo audit - -# Runs `cargo udeps` to check for unused dependencies -udeps: - cargo +nightly udeps --tests --all-targets --release - -# Performs a `cargo` clean and cleans the `ef_tests` directory. -clean: - cargo clean - make -C $(EF_TESTS) clean - make -C $(STATE_TRANSITION_VECTORS) clean From 8f3b17b859eceb954c019e322645e242f20f3c8b Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 May 2023 08:21:58 +0200 Subject: [PATCH 055/154] Update github CI to latest version --- .github/workflows/build.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c3205659a..8ec68e55e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ jobs: cargo-fmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get latest version of stable rust run: rustup update stable - name: Check formatting with cargofmt @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest needs: cargo-fmt steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get latest version of stable rust run: rustup update stable - name: Install protobuf compiler for the libp2p-core dependency @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest needs: cargo-fmt steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get latest version of stable rust run: rustup update stable - name: Run tests in release @@ -37,7 +37,7 @@ jobs: image: rust needs: cargo-fmt steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get latest version of stable rust run: rustup update stable - name: Install protobuf compiler for the libp2p-core dependency @@ -50,7 +50,7 @@ jobs: container: image: rust steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get latest version of stable rust run: rustup update stable - name: Check rustdoc links @@ -59,7 +59,7 @@ jobs: runs-on: ubuntu-latest needs: cargo-fmt steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Get latest version of nightly Rust run: rustup update nightly - name: Install cargo-udeps From 76f09533b526152a249a63de4f2b0af53fce4060 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 May 2023 08:26:22 +0200 Subject: [PATCH 056/154] Remove unused dependencies --- Cargo.lock | 139 +---------------------------------------------------- Cargo.toml | 8 --- 2 files changed, 2 insertions(+), 145 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b4d8e5cf..e68fbc1d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -236,17 +236,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "colored" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - [[package]] name = "const-oid" version = "0.9.2" @@ -344,7 +333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4355c25cbf99edcb6b4a0e906f6bdc6956eda149e84455bea49696429b2f8e8" dependencies = [ "futures", - "tokio-util 0.7.8", + "tokio-util", ] [[package]] @@ -388,17 +377,14 @@ dependencies = [ "clap", "delay_map", "enr", - "env_logger 0.9.3", "fnv", "futures", "hashlink", "hex", - "hex-literal", "hkdf", "lazy_static", "libp2p-core", "lru", - "mio", "more-asserts", "parking_lot 0.11.2", "parse-display-derive", @@ -408,14 +394,10 @@ dependencies = [ "rand_core 0.6.4", "rand_xorshift", "rlp", - "simple_logger", "smallvec", "socket2", "thiserror", "tokio", - "tokio-context", - "tokio-stream", - "tokio-util 0.6.10", "tracing", "tracing-subscriber", "uint", @@ -513,19 +495,6 @@ dependencies = [ "regex", ] -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "ff" version = "0.12.1" @@ -745,12 +714,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - [[package]] name = "hkdf" version = "0.12.3" @@ -790,12 +753,6 @@ dependencies = [ "hmac 0.8.1", ] -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "idna" version = "0.2.0" @@ -835,12 +792,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - [[package]] name = "k256" version = "0.11.6" @@ -1109,15 +1060,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "once_cell" version = "1.17.1" @@ -1405,7 +1347,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" dependencies = [ - "env_logger 0.7.1", + "env_logger", "log", "rand 0.7.3", "rand_core 0.5.1", @@ -1698,19 +1640,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "simple_logger" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45b60258a35dc3cb8a16890b8fd6723349bfa458d7960e25e633f1b1c19d7b5e" -dependencies = [ - "atty", - "colored", - "log", - "time", - "winapi", -] - [[package]] name = "slab" version = "0.4.8" @@ -1880,35 +1809,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "time" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" -dependencies = [ - "itoa", - "libc", - "num_threads", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - -[[package]] -name = "time-macros" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" -dependencies = [ - "time-core", -] - [[package]] name = "tokio" version = "1.28.0" @@ -1928,15 +1828,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "tokio-context" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf0b8394dd5ca9a1b726c629390154c19222dfd7467a4b56f1ced90adee3958" -dependencies = [ - "tokio", -] - [[package]] name = "tokio-macros" version = "2.1.0" @@ -1948,32 +1839,6 @@ dependencies = [ "syn 2.0.15", ] -[[package]] -name = "tokio-stream" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "slab", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.8" diff --git a/Cargo.toml b/Cargo.toml index b6fdc6c75..577d7c037 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,6 @@ exclude = [".gitignore", ".github/*"] [dependencies] enr = { version = "0.7.0", features = ["k256", "ed25519"] } tokio = { version = "1.15.0", features = ["net", "sync", "macros", "rt"] } -tokio-stream = "0.1.8" -tokio-util = { version = "0.6.9", features = ["time"] } libp2p-core = { version = "0.36.0", optional = true } zeroize = { version = "1.4.3", features = ["zeroize_derive"] } futures = "0.3.19" @@ -40,18 +38,12 @@ hashlink = "0.7.0" delay_map = "0.3.0" more-asserts = "0.2.2" async-trait = "0.1.67" -tokio-context = "0.1.3" -mio = "0.8.6" parse-display-derive = "0.8.0" thiserror = "1.0.40" [dev-dependencies] rand_07 = { package = "rand", version = "0.7" } quickcheck = "0.9.2" -env_logger = "0.9.0" -hex-literal = "0.3.4" -simple_logger = "1.16.0" -tokio-util = { version = "0.6.9", features = ["time"] } tokio = { version = "1.15.0", features = ["full"] } rand_xorshift = "0.3.0" rand_core = "0.6.3" From 37739a5ee0a7a21398ada7b09e69689e89650e1d Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 May 2023 12:30:08 +0200 Subject: [PATCH 057/154] Remove unused varaiable assignment --- src/handler/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 358ca120d..2adf6f776 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -835,11 +835,9 @@ impl Handler

{ ); if let Some(challenge) = self.active_challenges.remove(&node_address) { - let local_key = self.key.clone(); - let local_id = self.node_id; match Session::establish_from_challenge( - local_key, - &local_id, + self.key.clone(), + &self.node_id, &node_address.node_id, challenge, id_nonce_sig, From 3c820a0e3a8e42477bb8917f3d18d580f8b3c5ad Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 9 May 2023 18:12:47 +0200 Subject: [PATCH 058/154] Restore and improve config comments --- src/config.rs | 7 ++++--- src/service.rs | 15 +++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/config.rs b/src/config.rs index 9e69d3a00..2f9124234 100644 --- a/src/config.rs +++ b/src/config.rs @@ -29,7 +29,7 @@ pub struct Discv5Config { /// The number of retries for each UDP request. Default: 1. pub request_retries: u8, - /// The session timeout for each node in seconds. Default: 1 day. + /// The session timeout for each node. Default: 1 day. pub session_timeout: Duration, /// The maximum number of established sessions to maintain. Default: 1000. @@ -101,8 +101,9 @@ pub struct Discv5Config { pub executor: Option>, /// The max limit for peers with unreachable ENRs. Benevolent examples of such peers are peers - /// that are discovering their externally reachable socket and peers behind symmetric NAT. - /// Default is no limit. Minimum is 1. + /// that are discovering their externally reachable socket, nodes must assist at least one + /// such peer in discovering their reachable socket via ip voting, and peers behind symmetric + /// NAT. Default is no limit. Minimum is 1. pub unreachable_enr_limit: Option, } diff --git a/src/service.rs b/src/service.rs index 0a3f75c92..cec9405e2 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1095,13 +1095,16 @@ impl Service { for enr in nodes_to_send.into_iter() { let entry_size = rlp::encode(&enr).len(); // Responses assume that a session is established. Thus, on top of the encoded - // ENR's the packet should be a regular message. A regular message has an IV (16 - // bytes), and a header of 55 bytes. The find-nodes RPC requires 16 bytes for the ID and the - // `total` field. Also there is a 16 byte HMAC for encryption and an extra byte for - // RLP encoding. + // ENR's the packet should be a session message, which is the same data + // structure as a regular message. + // A session message has an IV (16 bytes), and a header of 55 bytes. The + // find-nodes RPC requires 16 bytes for the ID and the `total` field. Also there + // is a 16 byte HMAC for encryption and an extra byte for RLP encoding. + // + // We could also be responding via an authheader (this message could be in + // contained in a handshake message) which can take up to 282 bytes in + // the header, leaving even less space for the NODES response. // - // We could also be responding via an authheader which can take up to 282 bytes in its - // header. // As most messages will be normal messages we will try and pack as many ENR's we // can in and drop the response packet if a user requests an auth message of a very // packed response. From 0258cb23a843cf30f6f8db27a64d492840649476 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 9 May 2023 19:15:36 +0200 Subject: [PATCH 059/154] Use derive From macro --- Cargo.lock | 12 ++++++++++ Cargo.toml | 1 + src/config.rs | 4 ++-- src/error.rs | 19 ++++++++-------- src/lib.rs | 1 - src/macro_utils.rs | 11 ---------- src/rpc/mod.rs | 55 +++++++++++++++++++++++++++++++++++++++++----- src/socket/send.rs | 9 +++----- 8 files changed, 76 insertions(+), 36 deletions(-) delete mode 100644 src/macro_utils.rs diff --git a/Cargo.lock b/Cargo.lock index e68fbc1d5..31dc0df66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,6 +346,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.9.0" @@ -376,6 +387,7 @@ dependencies = [ "async-trait", "clap", "delay_map", + "derive_more", "enr", "fnv", "futures", diff --git a/Cargo.toml b/Cargo.toml index 577d7c037..00369b932 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ more-asserts = "0.2.2" async-trait = "0.1.67" parse-display-derive = "0.8.0" thiserror = "1.0.40" +derive_more = { version = "0.99.17", default-features = false, features = ["from"] } [dev-dependencies] rand_07 = { package = "rand", version = "0.7" } diff --git a/src/config.rs b/src/config.rs index 2f9124234..83b8c70d4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -101,8 +101,8 @@ pub struct Discv5Config { pub executor: Option>, /// The max limit for peers with unreachable ENRs. Benevolent examples of such peers are peers - /// that are discovering their externally reachable socket, nodes must assist at least one - /// such peer in discovering their reachable socket via ip voting, and peers behind symmetric + /// that are discovering their externally reachable socket, nodes must assist at least one + /// such peer in discovering their reachable socket via ip voting, and peers behind symmetric /// NAT. Default is no limit. Minimum is 1. pub unreachable_enr_limit: Option, } diff --git a/src/error.rs b/src/error.rs index 76cea6ae8..1de00cf56 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,8 +1,9 @@ use crate::{handler::Challenge, node_info::NonContactable}; +use derive_more::From; use rlp::DecoderError; use std::fmt; -#[derive(Debug)] +#[derive(Debug, From)] /// A general error that is used throughout the Discv5 library. pub enum Discv5Error { /// An invalid message type was received. @@ -14,6 +15,7 @@ pub enum Discv5Error { /// The public key type is known. UnknownPublicKey, /// The ENR key used is not supported. + #[from(ignore)] KeyTypeNotSupported(&'static str), /// Failed to derive an ephemeral public key. KeyDerivationFailed, @@ -34,33 +36,30 @@ pub enum Discv5Error { /// An RLP decoding error occurred. RLPError(DecoderError), /// Failed to encrypt a message. + #[from(ignore)] EncryptionFail(String), /// Failed to decrypt a message. + #[from(ignore)] DecryptionFailed(String), /// The custom error has occurred. + #[from(ignore)] Custom(&'static str), /// A generic dynamic error occurred. + #[from(ignore)] Error(String), /// An IO error occurred. Io(std::io::Error), } -impl From for Discv5Error { - fn from(err: std::io::Error) -> Discv5Error { - Discv5Error::Io(err) - } -} - macro_rules! impl_from_variant { - ($(<$($generic: ident$(: $trait: path)*,)+>)*, $from_type: ty, $to_type: ty, $variant: path) => { - impl$(<$($generic$(: $trait)*,)+>)* From<$from_type> for $to_type { + ($(<$($generic: ident,)+>)*, $from_type: ty, $to_type: ty, $variant: path) => { + impl$(<$($generic,)+>)* From<$from_type> for $to_type { fn from(_e: $from_type) -> Self { $variant } } }; } - impl_from_variant!(, tokio::sync::mpsc::error::SendError, Discv5Error, Self::ServiceChannelClosed); impl_from_variant!(, NonContactable, Discv5Error, Self::InvalidEnr); diff --git a/src/lib.rs b/src/lib.rs index 6d316f0e4..72d1e5308 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,7 +110,6 @@ pub mod handler; mod ipmode; pub mod kbucket; mod lru_time_cache; -mod macro_utils; pub mod metrics; mod node_info; pub mod packet; diff --git a/src/macro_utils.rs b/src/macro_utils.rs deleted file mode 100644 index 47fb49b39..000000000 --- a/src/macro_utils.rs +++ /dev/null @@ -1,11 +0,0 @@ -/// Implements From for an enum from some type. Takes the type to nest, the enum and the variant. -#[macro_export] -macro_rules! impl_from_variant_wrap { - ($from_type: ty, $to_type: ty, $variant: path) => { - impl From<$from_type> for $to_type { - fn from(t: $from_type) -> Self { - $variant(t) - } - } - }; -} diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index ef1c90753..d81f415c5 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -1,4 +1,4 @@ -use crate::impl_from_variant_wrap; +use derive_more::From; use parse_display_derive::Display; use rlp::{DecoderError, Rlp}; use std::convert::{TryFrom, TryInto}; @@ -55,7 +55,7 @@ where fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result; } -#[derive(Debug, Clone, PartialEq, Eq, Display)] +#[derive(Debug, Clone, PartialEq, Eq, Display, From)] /// A combined type representing the messages which are the payloads of packets. pub enum Message { /// A request, which contains its [`RequestId`]. @@ -69,10 +69,6 @@ pub enum Message { Notification(Notification), } -impl_from_variant_wrap!(Request, Message, Self::Request); -impl_from_variant_wrap!(Response, Message, Self::Response); -impl_from_variant_wrap!(Notification, Message, Self::Notification); - #[allow(dead_code)] impl Message { pub fn encode(self) -> Vec { @@ -107,6 +103,7 @@ impl Message { #[cfg(test)] mod tests { use super::*; + use crate::packet::MESSAGE_NONCE_LENGTH; use enr::{CombinedKey, Enr, EnrBuilder}; use std::net::IpAddr; @@ -346,4 +343,50 @@ mod tests { assert_eq!(request, decoded); } + + #[test] + fn test_enocde_decode_relay_init() { + // generate a new enr key for the initiator + let enr_key = CombinedKey::generate_secp256k1(); + // construct the initiator's ENR + let inr_enr = EnrBuilder::new("v4").build(&enr_key).unwrap(); + + // generate a new enr key for the target + let enr_key_tgt = CombinedKey::generate_secp256k1(); + // construct the target's ENR + let tgt_enr = EnrBuilder::new("v4").build(&enr_key_tgt).unwrap(); + let tgt_node_id = tgt_enr.node_id(); + + let nonce_bytes = hex::decode("47644922f5d6e951051051ac").unwrap(); + let mut nonce = [0u8; MESSAGE_NONCE_LENGTH]; + nonce[MESSAGE_NONCE_LENGTH - nonce_bytes.len()..].copy_from_slice(&nonce_bytes); + + let notif = Message::Notification(Notification::RelayInit(inr_enr, tgt_node_id, nonce)); + + println!("{notif}"); + + let encoded_notif = notif.clone().encode(); + let decoded_notif = Message::decode(&encoded_notif).expect("Should decode"); + + assert_eq!(notif, decoded_notif.into()); + } + + #[test] + fn test_enocde_decode_relay_msg() { + // generate a new enr key for the initiator + let enr_key = CombinedKey::generate_secp256k1(); + // construct the initiator's ENR + let inr_enr = EnrBuilder::new("v4").build(&enr_key).unwrap(); + + let nonce_bytes = hex::decode("9951051051aceb").unwrap(); + let mut nonce = [0u8; MESSAGE_NONCE_LENGTH]; + nonce[MESSAGE_NONCE_LENGTH - nonce_bytes.len()..].copy_from_slice(&nonce_bytes); + + let notif = Message::Notification(Notification::RelayMsg(inr_enr, nonce)); + + let encoded_notif = notif.clone().encode(); + let decoded_notif = Message::decode(&encoded_notif).expect("Should decode"); + + assert_eq!(notif, decoded_notif.into()); + } } diff --git a/src/socket/send.rs b/src/socket/send.rs index 9c70c9999..093619da7 100644 --- a/src/socket/send.rs +++ b/src/socket/send.rs @@ -1,7 +1,6 @@ //! This is a standalone task that encodes and sends Discv5 UDP packets -use crate::{ - impl_from_variant_wrap, metrics::METRICS, node_info::NodeAddress, packet::*, Executor, -}; +use crate::{metrics::METRICS, node_info::NodeAddress, packet::*, Executor}; +use derive_more::From; use std::{net::SocketAddr, sync::Arc}; use tokio::{ net::UdpSocket, @@ -9,6 +8,7 @@ use tokio::{ }; use tracing::{debug, trace, warn}; +#[derive(From)] pub enum Outbound { Packet(OutboundPacket), KeepHolePunched(SocketAddr), @@ -23,9 +23,6 @@ impl Outbound { } } -impl_from_variant_wrap!(OutboundPacket, Outbound, Self::Packet); -impl_from_variant_wrap!(SocketAddr, Outbound, Self::KeepHolePunched); - pub struct OutboundPacket { /// The destination node address pub node_address: NodeAddress, From 67b5d59e293e62bc4abadab88b898bf3ce8568ee Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 9 May 2023 19:19:47 +0200 Subject: [PATCH 060/154] fixup! Restore and improve config comments --- src/config.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 83b8c70d4..4ef79dd1f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -321,7 +321,8 @@ impl Discv5ConfigBuilder { self } - /// Whether to enable the incoming packet filter. + /// Sets the maximum number of session to peers with unreachable ENRs to allow. Minimum is 1 + /// peer. Default is no limit. pub fn unreachable_enr_limit(&mut self, peer_limit: Option) -> &mut Self { self.config.unreachable_enr_limit = peer_limit; self From 01d111b21e0b4d74a050f8f5994b8b9529891d4e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 9 May 2023 19:31:50 +0200 Subject: [PATCH 061/154] Restore drive-by commit to master --- src/error.rs | 2 +- src/ipmode.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index 1de00cf56..effe1df30 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,7 +10,7 @@ pub enum Discv5Error { InvalidMessage, /// An invalid ENR was received. InvalidEnr, - /// An invalid ENR was received. + /// The limit for sessions with peers that have an unreachable ENR is reached. LimitSessionsUnreachableEnr, /// The public key type is known. UnknownPublicKey, diff --git a/src/ipmode.rs b/src/ipmode.rs index b71ea5705..cf527d8fa 100644 --- a/src/ipmode.rs +++ b/src/ipmode.rs @@ -232,7 +232,7 @@ mod tests { } } -/// Copied from the standard library. See +/// Copied from the standard library. See https://github.com/rust-lang/rust/issues/27709 /// The current code is behind the `ip` feature. pub const fn to_ipv4_mapped(ip: &std::net::Ipv6Addr) -> Option { match ip.octets() { From 3eec2d50bf1e40e4bffe7217e67591d49a2dd4a2 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 9 May 2023 20:08:40 +0200 Subject: [PATCH 062/154] Return earlier from enr unreachable check --- src/config.rs | 2 +- src/handler/sessions/limiter.rs | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/config.rs b/src/config.rs index 4ef79dd1f..8f1f4d67f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -321,7 +321,7 @@ impl Discv5ConfigBuilder { self } - /// Sets the maximum number of session to peers with unreachable ENRs to allow. Minimum is 1 + /// Sets the maximum number of session to peers with unreachable ENRs to allow. Minimum is 1 /// peer. Default is no limit. pub fn unreachable_enr_limit(&mut self, peer_limit: Option) -> &mut Self { self.config.unreachable_enr_limit = peer_limit; diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs index 9f7a668a5..bf677f686 100644 --- a/src/handler/sessions/limiter.rs +++ b/src/handler/sessions/limiter.rs @@ -46,18 +46,19 @@ impl SessionLimiter { node_address: &NodeAddress, enr: &Enr, ) -> Result<(), Discv5Error> { + if enr.udp4_socket().is_none() || enr.udp6_socket().is_none() { + return Ok(()); + } // Empty buffer of expired sessions, and remove any which belong to unreachable ENRs. while let Ok(Some(session_index)) = self.rx_expired_sessions.try_next() { self.sessions_unreachable_enr_tracker.remove(&session_index); } - if enr.udp4_socket().is_none() && enr.udp6_socket().is_none() { - // Peer is unreachable - if self.sessions_unreachable_enr_tracker.len() >= self.limit { - return Err(Discv5Error::LimitSessionsUnreachableEnr); - } - self.sessions_unreachable_enr_tracker - .insert(node_address.clone()); + // Peer is unreachable + if self.sessions_unreachable_enr_tracker.len() >= self.limit { + return Err(Discv5Error::LimitSessionsUnreachableEnr); } + self.sessions_unreachable_enr_tracker + .insert(node_address.clone()); Ok(()) } } From 101bd6903290c025c857cd1391bd65e07c220a50 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 9 May 2023 20:21:17 +0200 Subject: [PATCH 063/154] Replace parse display with dep used in lighthouse --- .gitignore | 1 - Cargo.lock | 39 --------------------------------------- Cargo.toml | 3 +-- src/node_info.rs | 4 ++-- src/rpc/mod.rs | 9 ++++----- src/rpc/notification.rs | 6 +++--- src/rpc/request.rs | 4 ++-- src/rpc/response.rs | 4 ++-- 8 files changed, 14 insertions(+), 56 deletions(-) diff --git a/.gitignore b/.gitignore index be506a9f6..36b76a0f6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ # These are backup files generated by rustfmt **/*.rs.bk -Cargo.lock # VIM swap files *.sw[op] diff --git a/Cargo.lock b/Cargo.lock index 31dc0df66..dfdc24691 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,7 +399,6 @@ dependencies = [ "lru", "more-asserts", "parking_lot 0.11.2", - "parse-display-derive", "quickcheck", "rand 0.7.3", "rand 0.8.5", @@ -1155,21 +1154,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "parse-display-derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5587062be441f3d868f7c4c9d13c67f286b03aa679d7f8176ef80bf2ee79e5d" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "regex", - "regex-syntax 0.6.29", - "structmeta", - "syn 1.0.109", -] - [[package]] name = "percent-encoding" version = "2.1.0" @@ -1699,29 +1683,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "structmeta" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "104842d6278bf64aa9d2f182ba4bde31e8aec7a131d29b7f444bb9b344a09e2a" -dependencies = [ - "proc-macro2", - "quote", - "structmeta-derive", - "syn 1.0.109", -] - -[[package]] -name = "structmeta-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24420be405b590e2d746d83b01f09af673270cf80e9b003a5fa7b651c58c7d93" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "subtle" version = "2.4.1" diff --git a/Cargo.toml b/Cargo.toml index 00369b932..768daf7c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,9 +38,8 @@ hashlink = "0.7.0" delay_map = "0.3.0" more-asserts = "0.2.2" async-trait = "0.1.67" -parse-display-derive = "0.8.0" thiserror = "1.0.40" -derive_more = { version = "0.99.17", default-features = false, features = ["from"] } +derive_more = { version = "0.99.17", default-features = false, features = ["from", "display"] } [dev-dependencies] rand_07 = { package = "rand", version = "0.7" } diff --git a/src/node_info.rs b/src/node_info.rs index 4e6d39ded..8708ab0ef 100644 --- a/src/node_info.rs +++ b/src/node_info.rs @@ -1,7 +1,7 @@ use super::*; use crate::Enr; +use derive_more::Display; use enr::{CombinedPublicKey, NodeId}; -use parse_display_derive::Display; use std::net::SocketAddr; #[cfg(feature = "libp2p")] @@ -148,7 +148,7 @@ impl std::fmt::Display for NodeContact { /// A representation of an unsigned contactable node. #[derive(PartialEq, Hash, Eq, Clone, Debug, Display)] -#[display("Node: {node_id}, addr: {socket_addr}")] +#[display(fmt = "Node: {node_id}, addr: {socket_addr}")] pub struct NodeAddress { /// The destination socket address. pub socket_addr: SocketAddr, diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index d81f415c5..ca5e8e502 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -1,5 +1,4 @@ -use derive_more::From; -use parse_display_derive::Display; +use derive_more::{Display, From}; use rlp::{DecoderError, Rlp}; use std::convert::{TryFrom, TryInto}; @@ -59,13 +58,13 @@ where /// A combined type representing the messages which are the payloads of packets. pub enum Message { /// A request, which contains its [`RequestId`]. - #[display("{0}")] + #[display(fmt = "{_0}")] Request(Request), /// A Response, which contains the [`RequestId`] of its associated request. - #[display("{0}")] + #[display(fmt = "{_0}")] Response(Response), /// A unicast notification. - #[display("{0}")] + #[display(fmt = "{_0}")] Notification(Notification), } diff --git a/src/rpc/notification.rs b/src/rpc/notification.rs index 3be282022..dc29b2b22 100644 --- a/src/rpc/notification.rs +++ b/src/rpc/notification.rs @@ -3,8 +3,8 @@ use crate::{ packet::{MessageNonce, MESSAGE_NONCE_LENGTH}, Enr, }; +use derive_more::Display; use enr::NodeId; -use parse_display_derive::Display; use rlp::{DecoderError, Rlp, RlpStream}; use std::convert::TryInto; @@ -17,10 +17,10 @@ pub const NODE_ID_LENGTH: usize = 32; #[derive(Debug, Display, PartialEq, Eq, Clone)] pub enum Notification { /// A notification to initialise a one-shot relay circuit for hole-punching. - #[display("Notification: RelayInit: Initiator: {0}, Target: {1}, Nonce: {:2}")] + #[display(fmt = "Notification: RelayInit: Initiator: {_0}, Target: {_1}, Nonce: {_2:?}")] RelayInit(Enr, NodeId, NonceOfTimedOutMessage), /// The notification relayed to target of hole punch attempt. - #[display("Notification: RelayMsg: Initiator: {0}, Nonce: {:1}")] + #[display(fmt = "Notification: RelayMsg: Initiator: {_0}, Nonce: {_1:?}")] RelayMsg(Enr, NonceOfTimedOutMessage), } diff --git a/src/rpc/request.rs b/src/rpc/request.rs index 1ee1b45ba..26cd50983 100644 --- a/src/rpc/request.rs +++ b/src/rpc/request.rs @@ -1,12 +1,12 @@ use super::{MessageType, Payload}; -use parse_display_derive::Display; +use derive_more::Display; use rlp::{DecoderError, Rlp, RlpStream}; use std::convert::TryInto; use tracing::{debug, warn}; /// A request sent between nodes. #[derive(Debug, Clone, PartialEq, Eq, Display)] -#[display("Request: id: {id}: {body}")] +#[display(fmt = "Request: id: {id}: {body}")] pub struct Request { /// The [`RequestId`] of the request. pub id: RequestId, diff --git a/src/rpc/response.rs b/src/rpc/response.rs index 0025b0e92..a852f1b08 100644 --- a/src/rpc/response.rs +++ b/src/rpc/response.rs @@ -1,6 +1,6 @@ use super::{MessageType, Payload, RequestBody, RequestId}; use crate::Enr; -use parse_display_derive::Display; +use derive_more::Display; use rlp::{DecoderError, Rlp, RlpStream}; use std::{ convert::TryInto, @@ -10,7 +10,7 @@ use tracing::debug; /// A response sent in response to a [`super::Request`] #[derive(Debug, Clone, PartialEq, Eq, Display)] -#[display("Response: id: {id}: {body}")] +#[display(fmt = "Response: id: {id}: {body}")] pub struct Response { /// The [`RequestId`] of the request that triggered this response. pub id: RequestId, From fd62453014f76f6df064b8d403334f1b542ed199 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 9 May 2023 20:39:02 +0200 Subject: [PATCH 064/154] Safeguard with const evaluation --- src/handler/sessions/session.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index 30c73bd27..84cfb8cb6 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -83,6 +83,8 @@ impl Session { // If the message nonce length is ever set below 4 bytes this will explode. The packet // size constants shouldn't be modified. + const _: () = assert!(MESSAGE_NONCE_LENGTH > 4); + let random_nonce: [u8; MESSAGE_NONCE_LENGTH - 4] = rand::random(); let mut message_nonce: MessageNonce = [0u8; MESSAGE_NONCE_LENGTH]; message_nonce[..4].copy_from_slice(&self.counter.to_be_bytes()); @@ -125,6 +127,8 @@ impl Session { // If the message nonce length is ever set below 4 bytes this will explode. The packet // size constants shouldn't be modified. + const _: () = assert!(MESSAGE_NONCE_LENGTH > 4); + let random_nonce: [u8; MESSAGE_NONCE_LENGTH - 4] = rand::random(); let mut message_nonce: MessageNonce = [0u8; MESSAGE_NONCE_LENGTH]; message_nonce[..4].copy_from_slice(&self.counter.to_be_bytes()); From d9740f9fca7c4b501f718f07e610d165c79f6223 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 9 May 2023 20:47:18 +0200 Subject: [PATCH 065/154] Fix typo Co-authored-by: Divma <26765164+divagant-martian@users.noreply.github.com> --- src/packet/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packet/mod.rs b/src/packet/mod.rs index f6308e503..76b4de38a 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -142,7 +142,7 @@ pub enum PacketKind { enr_record: Option, }, /// A session message is a notification, hence it differs from the [`PacketKind::Message`] in - /// the way it handles sessions since notifications doesn't trigger responses, a session + /// the way it handles sessions since notifications don't trigger responses, a session /// message packet doesn't trigger a WHOAREYOU response. If a session doesn't exist to /// decrypt or encrypt a notification, it is dropped. SessionMessage { From d456f1a8dab8513fb508da7fc3c2c39151cbcb2b Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 12 May 2023 23:38:47 +0200 Subject: [PATCH 066/154] Mark unreachable code --- src/handler/sessions/session.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index 84cfb8cb6..e1b4842d7 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -271,7 +271,7 @@ impl Session { data: _, remote_enr, } = challenge; - remote_enr.unwrap() + remote_enr.expect("unreachable") } MostRecentEnr::Handshake(enr) => enr, }; From c651e2cf2194b886857ce9d2d16507bab9e00855 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 13 May 2023 00:12:38 +0200 Subject: [PATCH 067/154] Allow assertions on constants --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8ec68e55e..6d93e20c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,8 @@ jobs: - name: Install protobuf compiler for the libp2p-core dependency uses: arduino/setup-protoc@v1 - name: Lint code for quality and style with Clippy - run: cargo clippy --workspace --tests --all-features -- -D warnings + run: cargo clippy --workspace --tests --all-features -- -D warnings \ + -A clippy::assertions_on_constants release-tests-ubuntu: runs-on: ubuntu-latest needs: cargo-fmt From 74c8404ad9e738d251853b0dc98b007e15f415f4 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 13 May 2023 00:14:08 +0200 Subject: [PATCH 068/154] Fix clippy warnings --- src/rpc/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index ca5e8e502..d622eaa11 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -367,7 +367,7 @@ mod tests { let encoded_notif = notif.clone().encode(); let decoded_notif = Message::decode(&encoded_notif).expect("Should decode"); - assert_eq!(notif, decoded_notif.into()); + assert_eq!(notif, decoded_notif); } #[test] @@ -386,6 +386,6 @@ mod tests { let encoded_notif = notif.clone().encode(); let decoded_notif = Message::decode(&encoded_notif).expect("Should decode"); - assert_eq!(notif, decoded_notif.into()); + assert_eq!(notif, decoded_notif); } } From 1f7f86d11076eb87cd684e82efcc5a7244e2ca64 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 13 May 2023 08:12:30 +0200 Subject: [PATCH 069/154] fixup! Allow assertions on constants --- .github/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d93e20c9..558918fd4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,8 +21,7 @@ jobs: - name: Install protobuf compiler for the libp2p-core dependency uses: arduino/setup-protoc@v1 - name: Lint code for quality and style with Clippy - run: cargo clippy --workspace --tests --all-features -- -D warnings \ - -A clippy::assertions_on_constants + run: cargo clippy --workspace --tests --all-features -- -D warnings -A clippy::assertions_on_constants release-tests-ubuntu: runs-on: ubuntu-latest needs: cargo-fmt From 876c68a9596c091dcfba5712d24ffd309a43c2d5 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 13 May 2023 08:48:58 +0200 Subject: [PATCH 070/154] fixup! Return earlier from enr unreachable check --- src/handler/sessions/limiter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs index bf677f686..74fe610eb 100644 --- a/src/handler/sessions/limiter.rs +++ b/src/handler/sessions/limiter.rs @@ -46,7 +46,7 @@ impl SessionLimiter { node_address: &NodeAddress, enr: &Enr, ) -> Result<(), Discv5Error> { - if enr.udp4_socket().is_none() || enr.udp6_socket().is_none() { + if !enr.udp4_socket().is_none() || !enr.udp6_socket().is_none() { return Ok(()); } // Empty buffer of expired sessions, and remove any which belong to unreachable ENRs. From a6998da1af935b278c5cac08d1d615ab4d5598b2 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 13 May 2023 08:50:36 +0200 Subject: [PATCH 071/154] fixup! Fix session limiter constructor bug to not write to logs on no limit and add limiter test --- src/handler/sessions/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/sessions/mod.rs b/src/handler/sessions/mod.rs index 124d718e0..d7451a0e7 100644 --- a/src/handler/sessions/mod.rs +++ b/src/handler/sessions/mod.rs @@ -43,7 +43,7 @@ mod test { #[tokio::test] async fn test_limiter() { let max_nodes_unreachable_enr = 2; - let session_time_out = Duration::from_secs(10); + let session_time_out = Duration::from_secs(1); let mut sessions = Sessions::new(3, session_time_out, Some(max_nodes_unreachable_enr)); // first node From d13832adde40435f7f1538ab8850677d4b3cfa97 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 13 May 2023 08:51:50 +0200 Subject: [PATCH 072/154] fixup! Return earlier from enr unreachable check --- src/handler/sessions/limiter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs index 74fe610eb..8449e8a60 100644 --- a/src/handler/sessions/limiter.rs +++ b/src/handler/sessions/limiter.rs @@ -46,7 +46,7 @@ impl SessionLimiter { node_address: &NodeAddress, enr: &Enr, ) -> Result<(), Discv5Error> { - if !enr.udp4_socket().is_none() || !enr.udp6_socket().is_none() { + if enr.udp4_socket().is_some() || enr.udp6_socket().is_some() { return Ok(()); } // Empty buffer of expired sessions, and remove any which belong to unreachable ENRs. From fbf313fdd049ef5af56db8b45ed52aea6da9cfe5 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 13 May 2023 12:34:43 +0200 Subject: [PATCH 073/154] Shorten debug message --- src/socket/recv.rs | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/socket/recv.rs b/src/socket/recv.rs index c1d42e679..7dbbdedb4 100644 --- a/src/socket/recv.rs +++ b/src/socket/recv.rs @@ -143,26 +143,27 @@ impl RecvHandler { return; } // Decodes the packet - let (packet, authenticated_data) = match Packet::decode::

( - &self.node_id, - &self.recv_buffer[..length], - ) { - Ok(p) => p, - Err(e) => { - // This could be a packet to keep a NAT hole punched for this node in the - // sender's NAT, hence only serves purpose for the sender. - if length == 0 { - debug!("Appears to be a packet to keep a hole punched in sender's NAT, dropping. src: {}", src_address); - } else { - // Could not decode the packet, drop it. - debug!( - "Packet decoding failed, src: {}, error: {:?}", - src_address, e - ); + let (packet, authenticated_data) = + match Packet::decode::

(&self.node_id, &self.recv_buffer[..length]) { + Ok(p) => p, + Err(e) => { + // This could be a packet to keep a NAT hole punched for this node in the + // sender's NAT, hence only serves purpose for the sender. + if length == 0 { + debug!( + "Empty packet, possibly to keep a hole punched, dropping. src: {}", + src_address + ); + } else { + // Could not decode the packet, drop it. + debug!( + "Packet decoding failed, src: {}, error: {:?}", + src_address, e + ); + } + return; } - return; - } - }; + }; // If this is not a challenge packet, we immediately know its src_id and so pass it // through the second filter. From 9da9c1c4c51c9c2269a8b365cddc1f36d51d89da Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 19 May 2023 19:01:55 +0200 Subject: [PATCH 074/154] Correct comment correction --- src/service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service.rs b/src/service.rs index cec9405e2..56cb6c91d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -592,7 +592,7 @@ impl Service { to_request_enr = Some(enr); } } - // don't know of the ENR, drop the request + // don't know the peer, don't request its most recent ENR _ => {} } if let Some(enr) = to_request_enr { From 732bf7b3541b5243404bff3492619c5ff6288a7d Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 19 May 2023 19:07:19 +0200 Subject: [PATCH 075/154] Revert symbol --- src/socket/send.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/socket/send.rs b/src/socket/send.rs index 093619da7..0995767a7 100644 --- a/src/socket/send.rs +++ b/src/socket/send.rs @@ -70,7 +70,7 @@ impl SendHandler { loop { tokio::select! { Some(outbound) = self.handler_recv.recv() => { - let (dst_addr, encoded_pkt) = match outbound { + let (dst_addr, encoded_packet) = match outbound { Outbound::Packet(outbound_packet) => { let dst_id = outbound_packet.node_address.node_id; let encoded_packet = outbound_packet.packet.encode::

(&dst_id); @@ -82,10 +82,10 @@ impl SendHandler { } Outbound::KeepHolePunched(dst) => (dst, vec![]), }; - if let Err(e) = self.send.send_to(&encoded_pkt, &dst_addr).await { + if let Err(e) = self.send.send_to(&encoded_packet, &dst_addr).await { trace!("Could not send packet. Error: {:?}", e); } else { - METRICS.add_sent_bytes(encoded_pkt.len()); + METRICS.add_sent_bytes(encoded_packet.len()); } } _ = &mut self.exit => { From 8a8640b3e0d1c15fba65750747c3f13ee45223da Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 23 May 2023 18:07:58 +0200 Subject: [PATCH 076/154] Nitpick Co-authored-by: Divma <26765164+divagant-martian@users.noreply.github.com> --- src/service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service.rs b/src/service.rs index 56cb6c91d..7a327b3e1 100644 --- a/src/service.rs +++ b/src/service.rs @@ -868,7 +868,7 @@ impl Service { new_ip6, )) { - warn!("Failed to send socket update {}", e); + warn!("Failed to send socket update to handler: {}", e); }; } Err(e) => { From 709c51492c69d4c1406bb1553647560797d60e95 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 23 May 2023 18:05:26 +0200 Subject: [PATCH 077/154] Clarify comment --- src/service.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/service.rs b/src/service.rs index 7a327b3e1..d5463a2d5 100644 --- a/src/service.rs +++ b/src/service.rs @@ -861,7 +861,7 @@ impl Service { self.send_event(Discv5Event::SocketUpdated( new_ip6, )); - // Check if we are behind a NAT + // Notify Handler of socket update if let Err(e) = self.handler_send.send(HandlerIn::SocketUpdate( local_ip6_socket.map(SocketAddr::V6), @@ -889,7 +889,7 @@ impl Service { self.send_event(Discv5Event::SocketUpdated( new_ip4, )); - // Check if we are behind a NAT + // Notify Handler of socket update if let Err(e) = self.handler_send.send(HandlerIn::SocketUpdate( local_ip4_socket.map(SocketAddr::V4), From fdfddb426b6b710bc5f73fd33f94bffc92379ad5 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 23 May 2023 18:07:09 +0200 Subject: [PATCH 078/154] Reinsert Cargo.lock in gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 36b76a0f6..be506a9f6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ # These are backup files generated by rustfmt **/*.rs.bk +Cargo.lock # VIM swap files *.sw[op] From 207d096551de69a323ab2985e2928aee9cac5ef9 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 23 May 2023 18:12:33 +0200 Subject: [PATCH 079/154] Fix curly brace bug --- src/service.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/service.rs b/src/service.rs index d5463a2d5..fdc289936 100644 --- a/src/service.rs +++ b/src/service.rs @@ -903,9 +903,9 @@ impl Service { warn!("Failed to update local UDP socket. ip: {}, error: {:?}", new_ip4, e); } } - if updated { - self.ping_connected_peers(); - } + } + if updated { + self.ping_connected_peers(); } } } From 14180f8277629b64ec0762e602a24521468343d5 Mon Sep 17 00:00:00 2001 From: Diva M Date: Wed, 24 May 2023 11:50:08 -0500 Subject: [PATCH 080/154] remove lockfile --- Cargo.lock | 2197 ---------------------------------------------------- 1 file changed, 2197 deletions(-) delete mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index dfdc24691..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,2197 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aead" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" -dependencies = [ - "generic-array", -] - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if 1.0.0", - "cipher", - "cpufeatures", - "ctr", - "opaque-debug", -] - -[[package]] -name = "aes-gcm" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.9", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" - -[[package]] -name = "arc-swap" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62" - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "asn1_der" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" - -[[package]] -name = "async-trait" -version = "0.1.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] - -[[package]] -name = "clap" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "atty", - "bitflags", - "clap_derive", - "clap_lex", - "indexmap", - "once_cell", - "strsim", - "termcolor", - "textwrap", -] - -[[package]] -name = "clap_derive" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "const-oid" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "cpufeatures" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "ctr" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "data-encoding" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" - -[[package]] -name = "delay_map" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4355c25cbf99edcb6b4a0e906f6bdc6956eda149e84455bea49696429b2f8e8" -dependencies = [ - "futures", - "tokio-util", -] - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", - "subtle", -] - -[[package]] -name = "discv5" -version = "0.2.2" -dependencies = [ - "aes", - "aes-gcm", - "arrayvec", - "async-trait", - "clap", - "delay_map", - "derive_more", - "enr", - "fnv", - "futures", - "hashlink", - "hex", - "hkdf", - "lazy_static", - "libp2p-core", - "lru", - "more-asserts", - "parking_lot 0.11.2", - "quickcheck", - "rand 0.7.3", - "rand 0.8.5", - "rand_core 0.6.4", - "rand_xorshift", - "rlp", - "smallvec", - "socket2", - "thiserror", - "tokio", - "tracing", - "tracing-subscriber", - "uint", - "zeroize", -] - -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand 0.7.3", - "serde", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "either" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "digest 0.10.6", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "enr" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "492a7e5fc2504d5fdce8e124d3e263b244a68b283cac67a69eda0cd43e0aebad" -dependencies = [ - "base64", - "bs58", - "bytes", - "ed25519-dalek", - "hex", - "k256", - "log", - "rand 0.8.5", - "rlp", - "serde", - "sha3", - "zeroize", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", - "num_cpus", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "ghash" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" -dependencies = [ - "hashbrown 0.11.2", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "hmac-drbg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" -dependencies = [ - "digest 0.9.0", - "generic-array", - "hmac 0.8.1", -] - -[[package]] -name = "idna" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", - "sha2 0.10.6", -] - -[[package]] -name = "keccak" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.142" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" - -[[package]] -name = "libp2p-core" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1fff5bd889c82a0aec668f2045edd066f559d4e5c40354e5a4c77ac00caac38" -dependencies = [ - "asn1_der", - "bs58", - "ed25519-dalek", - "either", - "fnv", - "futures", - "futures-timer", - "instant", - "lazy_static", - "libsecp256k1", - "log", - "multiaddr", - "multihash", - "multistream-select", - "p256", - "parking_lot 0.12.1", - "pin-project", - "prost", - "prost-build", - "rand 0.8.5", - "rw-stream-sink", - "sha2 0.10.6", - "smallvec", - "thiserror", - "unsigned-varint", - "void", - "zeroize", -] - -[[package]] -name = "libsecp256k1" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" -dependencies = [ - "arrayref", - "base64", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde", - "sha2 0.9.9", - "typenum", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -dependencies = [ - "cfg-if 0.1.10", -] - -[[package]] -name = "lru" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" -dependencies = [ - "hashbrown 0.12.3", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "mio" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", -] - -[[package]] -name = "more-asserts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" - -[[package]] -name = "multiaddr" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" -dependencies = [ - "arrayref", - "bs58", - "byteorder", - "data-encoding", - "multihash", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", - "url", -] - -[[package]] -name = "multihash" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" -dependencies = [ - "core2", - "digest 0.10.6", - "multihash-derive", - "sha2 0.10.6", - "unsigned-varint", -] - -[[package]] -name = "multihash-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" -dependencies = [ - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", -] - -[[package]] -name = "multimap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce" - -[[package]] -name = "multistream-select" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "363a84be6453a70e63513660f4894ef815daf88e3356bffcda9ca27d810ce83b" -dependencies = [ - "bytes", - "futures", - "log", - "pin-project", - "smallvec", - "unsigned-varint", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "p256" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" -dependencies = [ - "ecdsa", - "elliptic-curve", - "sha2 0.10.6", -] - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "petgraph" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "polyval" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" - -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "proc-macro-crate" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" -dependencies = [ - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 1.0.109", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost", -] - -[[package]] -name = "quickcheck" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" -dependencies = [ - "env_logger", - "log", - "rand 0.7.3", - "rand_core 0.5.1", -] - -[[package]] -name = "quote" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.14", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.14", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.9", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "redox_syscall" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.1", -] - -[[package]] -name = "regex-automata" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" -dependencies = [ - "byteorder", - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" - -[[package]] -name = "remove_dir_all" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -dependencies = [ - "winapi", -] - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint", - "hmac 0.12.1", - "zeroize", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rustc-hex", -] - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rw-stream-sink" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" -dependencies = [ - "futures", - "pin-project", - "static_assertions", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "serde" -version = "1.0.160" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.160" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha3" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" -dependencies = [ - "digest 0.10.6", - "keccak", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook-registry" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" -dependencies = [ - "arc-swap", - "libc", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.6", - "rand_core 0.6.4", -] - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - -[[package]] -name = "tempfile" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "rand 0.7.3", - "redox_syscall 0.1.56", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - -[[package]] -name = "thiserror" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if 1.0.0", - "once_cell", -] - -[[package]] -name = "tokio" -version = "1.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" -dependencies = [ - "autocfg", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "tokio-util" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "slab", - "tokio", -] - -[[package]] -name = "toml" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" -dependencies = [ - "serde", -] - -[[package]] -name = "tracing" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" -dependencies = [ - "cfg-if 1.0.0", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -dependencies = [ - "matches", -] - -[[package]] -name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "unicode-normalization" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" -dependencies = [ - "smallvec", -] - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" - -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "unsigned-varint" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" - -[[package]] -name = "url" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" -dependencies = [ - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "version_check" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "which" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c14ef7e1b8b8ecfc75d5eca37949410046e66f15d185c01d70824f1f8111ef" -dependencies = [ - "libc", - "thiserror", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "zeroize" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] From 13f308af5fc557c3fb111213bcad72b146c91fce Mon Sep 17 00:00:00 2001 From: Diva M Date: Wed, 24 May 2023 11:54:45 -0500 Subject: [PATCH 081/154] reduce diff --- src/handler/mod.rs | 122 +++++++++++++++++++------------------ src/{rpc/mod.rs => rpc.rs} | 0 2 files changed, 62 insertions(+), 60 deletions(-) rename src/{rpc/mod.rs => rpc.rs} (100%) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 2adf6f776..cd52f5fc8 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1070,7 +1070,68 @@ impl Handler

{ authenticated_data: &[u8], ) { // check if we have an available session - let Some(session) = self.sessions.cache.get_mut(&node_address) else { + if let Some(session) = self.sessions.cache.get_mut(&node_address) { + // attempt to decrypt and process the message. + let message = match session.decrypt_message(message_nonce, message, authenticated_data) + { + Ok(m) => match Message::decode(&m) { + Ok(p) => p, + Err(e) => { + warn!("Failed to decode message. Error: {:?}, {}", e, node_address); + return; + } + }, + Err(e) => { + // We have a session, but the message could not be decrypted. It is likely the node + // sending this message has dropped their session. In this case, this message is a + // Random packet and we should reply with a WHOAREYOU. + // This means we need to drop the current session and re-establish. + trace!("Decryption failed. Error {}", e); + debug!( + "Message from node: {} is not encrypted with known session keys.", + node_address + ); + self.fail_session(&node_address, RequestError::InvalidRemotePacket, true) + .await; + // If we haven't already sent a WhoAreYou, + // spawn a WHOAREYOU event to check for highest known ENR + if self.active_challenges.get(&node_address).is_none() { + let whoareyou_ref = WhoAreYouRef(node_address, message_nonce); + if let Err(e) = self + .service_send + .send(HandlerOut::WhoAreYou(whoareyou_ref)) + .await + { + warn!("Failed to send WhoAreYou to the service {}", e) + } + } else { + trace!("WHOAREYOU packet already sent: {}", node_address); + } + return; + } + }; + + trace!("Received message from: {}", node_address); + + match message { + Message::Request(request) => { + // report the request to the application + if let Err(e) = self + .service_send + .send(HandlerOut::Request(node_address, Box::new(request))) + .await + { + warn!("Failed to report request to application {}", e) + } + } + _ => { + warn!( + "Peer sent message type that shouldn't be sent in packet type Message, {}", + node_address + ); + } + } + } else { // no session exists trace!("Received a message without a session. {}", node_address); trace!("Requesting a WHOAREYOU packet to be sent."); @@ -1088,65 +1149,6 @@ impl Handler

{ } return; }; - // attempt to decrypt and process the message. - let message = match session.decrypt_message(message_nonce, message, authenticated_data) { - Ok(m) => match Message::decode(&m) { - Ok(p) => p, - Err(e) => { - warn!("Failed to decode message. Error: {:?}, {}", e, node_address); - return; - } - }, - Err(e) => { - // We have a session, but the message could not be decrypted. It is likely the node - // sending this message has dropped their session. In this case, this message is a - // Random packet and we should reply with a WHOAREYOU. - // This means we need to drop the current session and re-establish. - trace!("Decryption failed. Error {}", e); - debug!( - "Message from node: {} is not encrypted with known session keys.", - node_address - ); - self.fail_session(&node_address, RequestError::InvalidRemotePacket, true) - .await; - // If we haven't already sent a WhoAreYou, - // spawn a WHOAREYOU event to check for highest known ENR - if self.active_challenges.get(&node_address).is_none() { - let whoareyou_ref = WhoAreYouRef(node_address, message_nonce); - if let Err(e) = self - .service_send - .send(HandlerOut::WhoAreYou(whoareyou_ref)) - .await - { - warn!("Failed to send WhoAreYou to the service {}", e) - } - } else { - trace!("WHOAREYOU packet already sent: {}", node_address); - } - return; - } - }; - - trace!("Received message from: {}", node_address); - - match message { - Message::Request(request) => { - // report the request to the application - if let Err(e) = self - .service_send - .send(HandlerOut::Request(node_address, Box::new(request))) - .await - { - warn!("Failed to report request to application {}", e) - } - } - _ => { - warn!( - "Peer sent message type that shouldn't be sent in packet type Message, {}", - node_address - ); - } - } } /// Handles a response to a request. Re-inserts the request call if the response is a multiple diff --git a/src/rpc/mod.rs b/src/rpc.rs similarity index 100% rename from src/rpc/mod.rs rename to src/rpc.rs From 0e40463355e187368e357e2b9bb6af51268ecdab Mon Sep 17 00:00:00 2001 From: Diva M Date: Wed, 24 May 2023 13:24:34 -0500 Subject: [PATCH 082/154] clippy --- src/handler/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index cd52f5fc8..79b2a157a 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1147,8 +1147,7 @@ impl Handler

{ e ) } - return; - }; + } } /// Handles a response to a request. Re-inserts the request call if the response is a multiple From a7ebf96c819255420ce9948570f1e2378cbad218 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 13:38:51 +0200 Subject: [PATCH 083/154] Fix typo Co-authored-by: Divma <26765164+divagant-martian@users.noreply.github.com> --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 8f1f4d67f..c97beaf9f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -321,7 +321,7 @@ impl Discv5ConfigBuilder { self } - /// Sets the maximum number of session to peers with unreachable ENRs to allow. Minimum is 1 + /// Sets the maximum number of sessions with peers with unreachable ENRs to allow. Minimum is 1 /// peer. Default is no limit. pub fn unreachable_enr_limit(&mut self, peer_limit: Option) -> &mut Self { self.config.unreachable_enr_limit = peer_limit; From 48f10d27bb9d0e0e97e26e40254f19d10759faff Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 13:57:21 +0200 Subject: [PATCH 084/154] Check limit in config instead --- src/config.rs | 7 +++++-- src/handler/mod.rs | 1 + src/handler/sessions/limiter.rs | 12 +----------- src/handler/sessions/mod.rs | 1 + 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/config.rs b/src/config.rs index 8f1f4d67f..a189a162b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,6 @@ use crate::{ - ipmode::IpMode, kbucket::MAX_NODES_PER_BUCKET, Enr, Executor, PermitBanList, RateLimiter, - RateLimiterBuilder, + handler::MIN_SESSIONS_UNREACHABLE_ENR, ipmode::IpMode, kbucket::MAX_NODES_PER_BUCKET, Enr, + Executor, PermitBanList, RateLimiter, RateLimiterBuilder, }; ///! A set of configuration parameters to tune the discovery protocol. use std::time::Duration; @@ -335,6 +335,9 @@ impl Discv5ConfigBuilder { }; assert!(self.config.incoming_bucket_limit <= MAX_NODES_PER_BUCKET); + if let Some(limit) = self.config.unreachable_enr_limit { + assert!(limit >= MIN_SESSIONS_UNREACHABLE_ENR); + } self.config.clone() } diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 2adf6f776..36d7e3d27 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -65,6 +65,7 @@ mod tests; use crate::metrics::METRICS; pub use crate::node_info::{NodeAddress, NodeContact}; +pub use sessions::MIN_SESSIONS_UNREACHABLE_ENR; use active_requests::ActiveRequests; use nat_hole_puncher::{Error as HolePunchError, NatHolePunch, NatHolePunchUtils}; diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs index 8449e8a60..af2a74bfc 100644 --- a/src/handler/sessions/limiter.rs +++ b/src/handler/sessions/limiter.rs @@ -1,6 +1,5 @@ use crate::{node_info::NodeAddress, Discv5Error, Enr}; use std::collections::HashSet; -use tracing::warn; /// The minimum number of peers to accept sessions with that have an unreachable ENR, i.e. cater /// requests for, at a time. Benevolent peers of this type could for example be symmetrically @@ -22,17 +21,8 @@ pub(crate) struct SessionLimiter { impl SessionLimiter { pub fn new( rx_expired_sessions: futures::channel::mpsc::Receiver, - unreachable_enr_limit: usize, + limit: usize, ) -> Self { - let limit = if unreachable_enr_limit < MIN_SESSIONS_UNREACHABLE_ENR { - warn!( - "unreachable ENR limit from config too low, setting to minimum {}", - MIN_SESSIONS_UNREACHABLE_ENR - ); - MIN_SESSIONS_UNREACHABLE_ENR - } else { - unreachable_enr_limit - }; SessionLimiter { sessions_unreachable_enr_tracker: Default::default(), rx_expired_sessions, diff --git a/src/handler/sessions/mod.rs b/src/handler/sessions/mod.rs index d7451a0e7..abf842efc 100644 --- a/src/handler/sessions/mod.rs +++ b/src/handler/sessions/mod.rs @@ -6,6 +6,7 @@ mod limiter; mod session; use limiter::SessionLimiter; +pub use limiter::MIN_SESSIONS_UNREACHABLE_ENR; pub use session::Session; pub struct Sessions { From 386775d7954f193462fc932e479acee55993c3d6 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 13:58:24 +0200 Subject: [PATCH 085/154] Restore comment --- src/handler/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 36d7e3d27..2f10b8ccf 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -137,7 +137,7 @@ pub enum HandlerOut { /// An RPC request failed. /// - /// This returns the request ID, an error indicating why the request failed. + /// This returns the request ID and an error indicating why the request failed. RequestFailed(RequestId, RequestError), /// A peer has supposed we have passed it another peer in a NODES response, if that is true From 72a9ecfa97070cf7d804e112e447232f3fdddc0e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 14:31:06 +0200 Subject: [PATCH 086/154] Simplify docs --- src/handler/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 2f10b8ccf..23ee7c91b 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -140,10 +140,8 @@ pub enum HandlerOut { /// This returns the request ID and an error indicating why the request failed. RequestFailed(RequestId, RequestError), - /// A peer has supposed we have passed it another peer in a NODES response, if that is true - /// (very probably not false) then the ENR of that peer is returned in a - /// [`HandlerIn::HolePunchEnr`]. Holds the NodeId to look up and the - /// [`Notification::RelayMsg`] we intend to relay to that peer if we find it. + /// Look-up an ENR in k-buckets. Passes the node id of the peer to look up and the + /// [`Notification`] we intend to send to it. FindHolePunchEnr(NodeId, Notification), } From 3926fce27c16990559c7c8bc0fd99e7ee05cefd3 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 14:49:44 +0200 Subject: [PATCH 087/154] Drop notification already at service layer --- src/handler/mod.rs | 19 ++++++++----------- src/service.rs | 16 +++++++++------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 23ee7c91b..70ae038c8 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -107,9 +107,9 @@ pub enum HandlerIn { /// be returned here to submit the application's response. WhoAreYou(WhoAreYouRef, Option), - /// Response to a [`HandlerOut::FindHolePunchEnr`]. Returns the ENR if it was found. Also - /// returns the [`Notification::RelayMsg`] we intend to relay to that peer if it was found. - HolePunchEnr(Option, Notification), + /// Response to a [`HandlerOut::FindHolePunchEnr`]. Returns the ENR and the + /// [`Notification::RelayMsg`] we intend to relay to that peer. + HolePunchEnr(Enr, Notification), /// Observed socket has been update. The old socket and the current socket. SocketUpdate(Option, SocketAddr), @@ -140,8 +140,8 @@ pub enum HandlerOut { /// This returns the request ID and an error indicating why the request failed. RequestFailed(RequestId, RequestError), - /// Look-up an ENR in k-buckets. Passes the node id of the peer to look up and the - /// [`Notification`] we intend to send to it. + /// Look-up an ENR in k-buckets. Passes the node id of the peer to look up and the + /// [`Notification::RelayMsg`] we intend to send to it. FindHolePunchEnr(NodeId, Notification), } @@ -329,7 +329,7 @@ impl Handler

{ HandlerIn::Response(dst, response) => self.send_response(dst, *response).await, HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge(wru_ref, enr).await, HandlerIn::HolePunchEnr(tgt_enr, relay_msg_notif) => { - if let Err(e) = self.on_hole_punch_tgt_enr(tgt_enr, relay_msg_notif).await { + if let Err(e) = self.send_relay_msg_notif(tgt_enr, relay_msg_notif).await { warn!("Failed to relay. Error: {}", e); } } @@ -1362,14 +1362,11 @@ impl Handler

{ .retain(|_, time| time.is_none() || Some(Instant::now()) < *time); } - async fn on_hole_punch_tgt_enr( + async fn send_relay_msg_notif( &mut self, - tgt_enr: Option, + tgt_enr: Enr, relay_msg_notif: Notification, ) -> Result<(), HolePunchError> { - let Some(tgt_enr) = tgt_enr else { - return Err(HolePunchError::Relay(Discv5Error::Custom("Target enr not found"))); - }; let tgt_node_address = match NodeContact::try_from_enr(tgt_enr, self.nat_hole_puncher.ip_mode) { Ok(contact) => contact.node_address(), diff --git a/src/service.rs b/src/service.rs index fdc289936..b84a837a3 100644 --- a/src/service.rs +++ b/src/service.rs @@ -405,14 +405,16 @@ impl Service { self.rpc_failure(request_id, error); } HandlerOut::FindHolePunchEnr(tgt_node_id, relay_msg_notif) => { - // check if we know this node id in our routing table + // check if we know this node id in our routing table, otherwise drop + // notification. + // todo(emhane): ban peers that ask us to relay to a peer we very + // unlikely could have sent to them in a NODES response. let key = kbucket::Key::from(tgt_node_id); - let tgt_enr = match self.kbuckets.write().entry(&key) { - kbucket::Entry::Present(entry, _) => Some(entry.value().clone()), - _ => None - }; - if let Err(e) = self.handler_send.send(HandlerIn::HolePunchEnr(tgt_enr, relay_msg_notif)) { - warn!("Failed to send target enr to relay proccess, error: {}", e); + if let kbucket::Entry::Present(entry, _) = self.kbuckets.write().entry(&key) { + let enr = entry.value().clone(); + if let Err(e) = self.handler_send.send(HandlerIn::HolePunchEnr(enr, relay_msg_notif)) { + warn!("Failed to send target enr to relay proccess, error: {}", e); + } } } } From d2612e9dbb9bb48fdf3eef6a1daf65bd927f1bcd Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 14:50:34 +0200 Subject: [PATCH 088/154] Improve rust idiomacy --- src/handler/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 70ae038c8..d669411a7 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -215,10 +215,10 @@ pub struct Handler { socket: Socket, /// Exit channel to shutdown the handler. exit: oneshot::Receiver<()>, - /// Access generic when implementing traits for Handler. - _phantom: PhantomData

, /// Types necessary to plug in nat hole punching. nat_hole_puncher: NatHolePunchUtils, + /// Access generic when implementing traits for Handler. + _phantom: PhantomData

, } type HandlerReturn = ( From 8961aae1f4df367276e0406dd976533231f311d2 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 14:53:19 +0200 Subject: [PATCH 089/154] fixup! Drop notification already at service layer --- src/service.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/service.rs b/src/service.rs index b84a837a3..3829b5b8f 100644 --- a/src/service.rs +++ b/src/service.rs @@ -415,6 +415,8 @@ impl Service { if let Err(e) = self.handler_send.send(HandlerIn::HolePunchEnr(enr, relay_msg_notif)) { warn!("Failed to send target enr to relay proccess, error: {}", e); } + } else { + warn!("Peer {tgt_node_id} requested relaying to a peer not in k-buckets, {relay_msg_notif}"); } } } From d68ce6b67955f4867762c702022dfe2f46c1107b Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 14:56:06 +0200 Subject: [PATCH 090/154] Nitpick --- src/handler/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index d669411a7..26f6ca865 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -107,7 +107,7 @@ pub enum HandlerIn { /// be returned here to submit the application's response. WhoAreYou(WhoAreYouRef, Option), - /// Response to a [`HandlerOut::FindHolePunchEnr`]. Returns the ENR and the + /// A response to a [`HandlerOut::FindHolePunchEnr`]. Returns the ENR and the /// [`Notification::RelayMsg`] we intend to relay to that peer. HolePunchEnr(Enr, Notification), From e79df73602f79c7a6152b71342ab0bed1ca95b8e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 15:33:05 +0200 Subject: [PATCH 091/154] Remove hole punch trait --- Cargo.lock | 396 +++++++++++++++++++------------- Cargo.toml | 1 - src/handler/mod.rs | 162 +------------ src/handler/nat_hole_puncher.rs | 197 +++++++++++++--- 4 files changed, 403 insertions(+), 353 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dfdc24691..e32002b09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,7 +17,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cipher", "cpufeatures", "ctr", @@ -60,21 +60,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.31" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" - -[[package]] -name = "arc-swap" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -88,24 +82,13 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" -[[package]] -name = "async-trait" -version = "0.1.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -136,9 +119,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" @@ -177,10 +160,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] -name = "cfg-if" -version = "0.1.10" +name = "cc" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -322,9 +305,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.2.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "delay_map" @@ -368,9 +351,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common", @@ -384,7 +367,6 @@ dependencies = [ "aes", "aes-gcm", "arrayvec", - "async-trait", "clap", "delay_map", "derive_more", @@ -452,9 +434,9 @@ dependencies = [ [[package]] name = "either" -version = "1.5.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" @@ -465,7 +447,7 @@ dependencies = [ "base16ct", "crypto-bigint", "der", - "digest 0.10.6", + "digest 0.10.7", "ff", "generic-array", "group", @@ -506,6 +488,36 @@ dependencies = [ "regex", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "ff" version = "0.12.1" @@ -528,6 +540,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.28" @@ -585,7 +606,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -636,11 +657,11 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -651,7 +672,7 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.11.0+wasi-snapshot-preview1", ] @@ -712,13 +733,28 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.1.13" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -750,7 +786,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -766,11 +802,10 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -791,7 +826,18 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", ] [[package]] @@ -809,7 +855,7 @@ version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "ecdsa", "elliptic-curve", "sha2 0.10.6", @@ -817,9 +863,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] @@ -832,9 +878,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libp2p-core" @@ -919,6 +965,12 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + [[package]] name = "lock_api" version = "0.4.9" @@ -931,11 +983,11 @@ dependencies = [ [[package]] name = "log" -version = "0.4.8" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", ] [[package]] @@ -956,12 +1008,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" - [[package]] name = "memchr" version = "2.5.0" @@ -1011,7 +1057,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" dependencies = [ "core2", - "digest 0.10.6", + "digest 0.10.7", "multihash-derive", "sha2 0.10.6", "unsigned-varint", @@ -1033,9 +1079,9 @@ dependencies = [ [[package]] name = "multimap" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "multistream-select" @@ -1063,11 +1109,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -1133,7 +1179,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "instant", "libc", "redox_syscall 0.2.16", @@ -1147,7 +1193,7 @@ version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall 0.2.16", "smallvec", @@ -1156,9 +1202,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "petgraph" @@ -1172,22 +1218,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.16", ] [[package]] @@ -1218,7 +1264,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "opaque-debug", "universal-hash", @@ -1226,9 +1272,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.8" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" @@ -1276,9 +1322,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" dependencies = [ "unicode-ident", ] @@ -1351,9 +1397,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" dependencies = [ "proc-macro2", ] @@ -1364,7 +1410,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.14", + "getrandom 0.1.16", "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", @@ -1408,7 +1454,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.14", + "getrandom 0.1.16", ] [[package]] @@ -1440,37 +1486,39 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.56" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.1", + "regex-syntax 0.7.2", ] [[package]] name = "regex-automata" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "byteorder", "regex-syntax 0.6.29", ] @@ -1482,18 +1530,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" - -[[package]] -name = "remove_dir_all" -version = "0.5.2" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -dependencies = [ - "winapi", -] +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "rfc6979" @@ -1522,6 +1561,20 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustix" +version = "0.37.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "rw-stream-sink" version = "0.3.0" @@ -1555,22 +1608,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.160" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1580,7 +1633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -1592,18 +1645,18 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] name = "sha3" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "keccak", ] @@ -1618,11 +1671,10 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.2.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ - "arc-swap", "libc", ] @@ -1632,7 +1684,7 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -1702,9 +1754,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" dependencies = [ "proc-macro2", "quote", @@ -1725,16 +1777,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.1.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ - "cfg-if 0.1.10", - "libc", - "rand 0.7.3", - "redox_syscall 0.1.56", - "remove_dir_all", - "winapi", + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", ] [[package]] @@ -1769,7 +1820,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1778,15 +1829,30 @@ version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "once_cell", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" -version = "1.28.0" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" +checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ "autocfg", "bytes", @@ -1809,7 +1875,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1828,20 +1894,20 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.6" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] [[package]] name = "tracing" -version = "0.1.35" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -1856,14 +1922,14 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -1918,33 +1984,30 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.4" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -dependencies = [ - "matches", -] +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-normalization" -version = "0.1.12" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ - "smallvec", + "tinyvec", ] [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" @@ -1964,12 +2027,12 @@ checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" [[package]] name = "url" -version = "2.1.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ + "form_urlencoded", "idna", - "matches", "percent-encoding", ] @@ -1981,9 +2044,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "void" @@ -2005,12 +2068,13 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "which" -version = "4.0.2" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c14ef7e1b8b8ecfc75d5eca37949410046e66f15d185c01d70824f1f8111ef" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ + "either", "libc", - "thiserror", + "once_cell", ] [[package]] @@ -2193,5 +2257,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] diff --git a/Cargo.toml b/Cargo.toml index 768daf7c1..b73a2de80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,6 @@ lru = "0.7.1" hashlink = "0.7.0" delay_map = "0.3.0" more-asserts = "0.2.2" -async-trait = "0.1.67" thiserror = "1.0.40" derive_more = { version = "0.99.17", default-features = false, features = ["from", "display"] } diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 26f6ca865..3cd70aa71 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -38,7 +38,6 @@ use crate::{ socket::{FilterConfig, Outbound, Socket}, Enr, }; -use async_trait::async_trait; use delay_map::HashMapDelay; use enr::{CombinedKey, NodeId}; use futures::prelude::*; @@ -68,7 +67,7 @@ pub use crate::node_info::{NodeAddress, NodeContact}; pub use sessions::MIN_SESSIONS_UNREACHABLE_ENR; use active_requests::ActiveRequests; -use nat_hole_puncher::{Error as HolePunchError, NatHolePunch, NatHolePunchUtils}; +use nat_hole_puncher::NatHolePunchUtils; use request_call::RequestCall; use sessions::{Session, Sessions}; @@ -329,7 +328,7 @@ impl Handler

{ HandlerIn::Response(dst, response) => self.send_response(dst, *response).await, HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge(wru_ref, enr).await, HandlerIn::HolePunchEnr(tgt_enr, relay_msg_notif) => { - if let Err(e) = self.send_relay_msg_notif(tgt_enr, relay_msg_notif).await { + if let Err(e) = nat_hole_puncher::send_relay_msg_notif(self, tgt_enr, relay_msg_notif).await { warn!("Failed to relay. Error: {}", e); } } @@ -363,7 +362,7 @@ impl Handler

{ self.send_next_request(node_address).await; } Some(Ok(peer_socket)) = self.nat_hole_puncher.next() => { - if let Err(e) = self.on_hole_punch_expired(peer_socket).await { + if let Err(e) = nat_hole_puncher::on_hole_punch_expired(self, peer_socket).await { warn!("Failed to keep hole punched for peer, error: {}", e); } } @@ -480,8 +479,7 @@ impl Handler

{ trace!("Trying to hole punch target {target} with relay {relay}"); let local_enr = self.enr.read().clone(); let nonce = request_call.packet().header.message_nonce; - match self - .on_request_time_out(relay, local_enr, nonce, target) + match nat_hole_puncher::on_request_time_out(self, relay, local_enr, nonce, target) .await { Err(e) => { @@ -1041,10 +1039,10 @@ impl Handler

{ Message::Notification(notif) => { let res = match notif { Notification::RelayInit(initr, tgt, timed_out_nonce) => { - self.on_relay_init(initr, tgt, timed_out_nonce).await + nat_hole_puncher::on_relay_init(self, initr, tgt, timed_out_nonce).await } Notification::RelayMsg(initr, timed_out_nonce) => { - self.on_relay_msg(initr, timed_out_nonce).await + nat_hole_puncher::on_relay_msg(self, initr, timed_out_nonce).await } }; if let Err(e) = res { @@ -1362,42 +1360,6 @@ impl Handler

{ .retain(|_, time| time.is_none() || Some(Instant::now()) < *time); } - async fn send_relay_msg_notif( - &mut self, - tgt_enr: Enr, - relay_msg_notif: Notification, - ) -> Result<(), HolePunchError> { - let tgt_node_address = - match NodeContact::try_from_enr(tgt_enr, self.nat_hole_puncher.ip_mode) { - Ok(contact) => contact.node_address(), - Err(e) => return Err(HolePunchError::Relay(e.into())), - }; - if let Some(session) = self.sessions.cache.get_mut(&tgt_node_address) { - trace!( - "Sending notif to target {}. relay msg: {}", - tgt_node_address.node_id, - relay_msg_notif, - ); - // Encrypt the notification and send - let packet = - match session.encrypt_notification::

(self.node_id, &relay_msg_notif.encode()) { - Ok(packet) => packet, - Err(e) => { - return Err(HolePunchError::Relay(e)); - } - }; - self.send(tgt_node_address, packet).await; - Ok(()) - } else { - // Either the session is being established or has expired. We simply drop the - // notification in this case to ensure hole punch round-trip time stays within the - // time out of the udp entrypoint for the target peer in the initiator's NAT, set by - // the original timed out FINDNODE request from the initiator, as the initiator may - // also be behind a NAT. - Err(HolePunchError::Relay(Discv5Error::SessionNotEstablished)) - } - } - async fn new_connection( &mut self, enr: Enr, @@ -1416,115 +1378,3 @@ impl Handler

{ } } } - -#[async_trait] -impl NatHolePunch for Handler

{ - async fn on_request_time_out( - &mut self, - relay: NodeAddress, - local_enr: Enr, - timed_out_nonce: MessageNonce, - target_session_index: NodeAddress, - ) -> Result<(), HolePunchError> { - // Another hole punch process with this target may have just completed. - if self.sessions.cache.get(&target_session_index).is_some() { - return Ok(()); - } - if let Some(session) = self.sessions.cache.get_mut(&relay) { - let relay_init_notif = - Notification::RelayInit(local_enr, target_session_index.node_id, timed_out_nonce); - trace!( - "Sending notif to relay {}. relay init: {}", - relay.node_id, - relay_init_notif, - ); - // Encrypt the message and send - let packet = - match session.encrypt_notification::

(self.node_id, &relay_init_notif.encode()) { - Ok(packet) => packet, - Err(e) => { - return Err(HolePunchError::Initiator(e)); - } - }; - self.send(relay, packet).await; - } else { - // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays - // within the time out of the udp entrypoint for the target peer in the initiator's - // router, set by the original timed out FINDNODE request from the initiator, as the - // initiator may also be behind a NAT. - warn!( - "Session is not established. Dropping relay notification for relay: {}", - relay.node_id - ); - } - Ok(()) - } - - async fn on_relay_init( - &mut self, - initr: Enr, - tgt: NodeId, - timed_out_nonce: MessageNonce, - ) -> Result<(), HolePunchError> { - // Assemble the notification for the target - let relay_msg_notif = Notification::RelayMsg(initr, timed_out_nonce); - - // Check for target peer in our kbuckets otherwise drop notification. - if let Err(e) = self - .service_send - .send(HandlerOut::FindHolePunchEnr(tgt, relay_msg_notif)) - .await - { - return Err(HolePunchError::Relay(e.into())); - } - Ok(()) - } - - async fn on_relay_msg( - &mut self, - initr: Enr, - timed_out_nonce: MessageNonce, - ) -> Result<(), HolePunchError> { - let initiator_node_address = - match NodeContact::try_from_enr(initr, self.nat_hole_puncher.ip_mode) { - Ok(contact) => contact.node_address(), - Err(e) => return Err(HolePunchError::Target(e.into())), - }; - - // A session may already have been established. - if self.sessions.cache.get(&initiator_node_address).is_some() { - trace!( - "Session already established with initiator: {}", - initiator_node_address - ); - return Ok(()); - } - // Possibly, an attempt to punch this hole, using another relay, is in progress. - if self - .active_challenges - .get(&initiator_node_address) - .is_some() - { - trace!( - "WHOAREYOU packet already sent to initiator: {}", - initiator_node_address - ); - return Ok(()); - } - // If not hole punch attempts are in progress, spawn a WHOAREYOU event to punch a hole in - // our NAT for initiator. - let whoareyou_ref = WhoAreYouRef(initiator_node_address, timed_out_nonce); - if let Err(e) = self - .service_send - .send(HandlerOut::WhoAreYou(whoareyou_ref)) - .await - { - return Err(HolePunchError::Target(e.into())); - } - Ok(()) - } - async fn on_hole_punch_expired(&mut self, dst: SocketAddr) -> Result<(), HolePunchError> { - self.send_outbound(dst.into()).await; - Ok(()) - } -} diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index b2b21f824..fc9a22a51 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -1,5 +1,10 @@ -use crate::{node_info::NodeAddress, packet::MessageNonce, Discv5Error, Enr, IpMode}; -use async_trait::async_trait; +use crate::{ + handler::{Handler, HandlerOut, WhoAreYouRef}, + node_info::{NodeAddress, NodeContact}, + packet::MessageNonce, + rpc::{Notification, Payload}, + Discv5Error, Enr, IpMode, ProtocolIdentity, +}; use delay_map::HashSetDelay; use enr::NodeId; use futures::{Stream, StreamExt}; @@ -14,6 +19,7 @@ use std::{ time::Duration, }; use thiserror::Error; +use tracing::{trace, warn}; /// The expected shortest lifetime in most NAT configurations of a punched hole in seconds. pub const DEFAULT_HOLE_PUNCH_LIFETIME: u64 = 20; @@ -33,34 +39,165 @@ pub enum Error { Target(Discv5Error), } -#[async_trait] -pub trait NatHolePunch { - /// A request times out. Should trigger the initiation of a hole punch attempt, given a - /// transitive route to the target exists. - async fn on_request_time_out( - &mut self, - relay: NodeAddress, - local_enr: Enr, // initiator-enr - timed_out_nonce: MessageNonce, - target_session_index: NodeAddress, - ) -> Result<(), Error>; - /// A RelayInit notification is received over discv5 indicating this node is the relay. Should - /// trigger sending a RelayMsg to the target. - async fn on_relay_init( - &mut self, - initr: Enr, - tgt: NodeId, - timed_out_nonce: MessageNonce, - ) -> Result<(), Error>; - /// A RelayMsg notification is received over discv5 indicating this node is the target. Should - /// trigger a WHOAREYOU to be sent to the initiator using the `nonce` in the RelayMsg. - async fn on_relay_msg( - &mut self, - initr: Enr, - timed_out_nonce: MessageNonce, - ) -> Result<(), Error>; - /// A punched hole closes. Should trigger an empty packet to be sent to the peer. - async fn on_hole_punch_expired(&mut self, dst: SocketAddr) -> Result<(), Error>; +/// A request times out. Should trigger the initiation of a hole punch attempt, given a +/// transitive route to the target exists. +pub async fn on_request_time_out( + handler: &mut Handler

, + relay: NodeAddress, + local_enr: Enr, // initiator-enr + timed_out_nonce: MessageNonce, + target_session_index: NodeAddress, +) -> Result<(), Error> { + // Another hole punch process with this target may have just completed. + if handler.sessions.cache.get(&target_session_index).is_some() { + return Ok(()); + } + if let Some(session) = handler.sessions.cache.get_mut(&relay) { + let relay_init_notif = + Notification::RelayInit(local_enr, target_session_index.node_id, timed_out_nonce); + trace!( + "Sending notif to relay {}. relay init: {}", + relay.node_id, + relay_init_notif, + ); + // Encrypt the message and send + let packet = + match session.encrypt_notification::

(handler.node_id, &relay_init_notif.encode()) { + Ok(packet) => packet, + Err(e) => { + return Err(Error::Initiator(e)); + } + }; + handler.send(relay, packet).await; + } else { + // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays + // within the time out of the udp entrypoint for the target peer in the initiator's + // router, set by the original timed out FINDNODE request from the initiator, as the + // initiator may also be behind a NAT. + warn!( + "Session is not established. Dropping relay notification for relay: {}", + relay.node_id + ); + } + Ok(()) +} + +/// A RelayInit notification is received over discv5 indicating this node is the relay. Should +/// trigger sending a RelayMsg to the target. +pub async fn on_relay_init( + handler: &mut Handler

, + initr: Enr, + tgt: NodeId, + timed_out_nonce: MessageNonce, +) -> Result<(), Error> { + // Assemble the notification for the target + let relay_msg_notif = Notification::RelayMsg(initr, timed_out_nonce); + + // Check for target peer in our kbuckets otherwise drop notification. + if let Err(e) = handler + .service_send + .send(HandlerOut::FindHolePunchEnr(tgt, relay_msg_notif)) + .await + { + return Err(Error::Relay(e.into())); + } + Ok(()) +} + +/// A RelayMsg notification is received over discv5 indicating this node is the target. Should +/// trigger a WHOAREYOU to be sent to the initiator using the `nonce` in the RelayMsg. +pub async fn on_relay_msg( + handler: &mut Handler

, + initr: Enr, + timed_out_nonce: MessageNonce, +) -> Result<(), Error> { + let initiator_node_address = + match NodeContact::try_from_enr(initr, handler.nat_hole_puncher.ip_mode) { + Ok(contact) => contact.node_address(), + Err(e) => return Err(Error::Target(e.into())), + }; + + // A session may already have been established. + if handler + .sessions + .cache + .get(&initiator_node_address) + .is_some() + { + trace!( + "Session already established with initiator: {}", + initiator_node_address + ); + return Ok(()); + } + // Possibly, an attempt to punch this hole, using another relay, is in progress. + if handler + .active_challenges + .get(&initiator_node_address) + .is_some() + { + trace!( + "WHOAREYOU packet already sent to initiator: {}", + initiator_node_address + ); + return Ok(()); + } + // If not hole punch attempts are in progress, spawn a WHOAREYOU event to punch a hole in + // our NAT for initiator. + let whoareyou_ref = WhoAreYouRef(initiator_node_address, timed_out_nonce); + if let Err(e) = handler + .service_send + .send(HandlerOut::WhoAreYou(whoareyou_ref)) + .await + { + return Err(Error::Target(e.into())); + } + Ok(()) +} + +pub async fn send_relay_msg_notif( + handler: &mut Handler

, + tgt_enr: Enr, + relay_msg_notif: Notification, +) -> Result<(), Error> { + let tgt_node_address = + match NodeContact::try_from_enr(tgt_enr, handler.nat_hole_puncher.ip_mode) { + Ok(contact) => contact.node_address(), + Err(e) => return Err(Error::Relay(e.into())), + }; + if let Some(session) = handler.sessions.cache.get_mut(&tgt_node_address) { + trace!( + "Sending notif to target {}. relay msg: {}", + tgt_node_address.node_id, + relay_msg_notif, + ); + // Encrypt the notification and send + let packet = + match session.encrypt_notification::

(handler.node_id, &relay_msg_notif.encode()) { + Ok(packet) => packet, + Err(e) => { + return Err(Error::Relay(e)); + } + }; + handler.send(tgt_node_address, packet).await; + Ok(()) + } else { + // Either the session is being established or has expired. We simply drop the + // notification in this case to ensure hole punch round-trip time stays within the + // time out of the udp entrypoint for the target peer in the initiator's NAT, set by + // the original timed out FINDNODE request from the initiator, as the initiator may + // also be behind a NAT. + Err(Error::Relay(Discv5Error::SessionNotEstablished)) + } +} + +/// A punched hole closes. Should trigger an empty packet to be sent to the peer. +pub async fn on_hole_punch_expired( + handler: &mut Handler

, + dst: SocketAddr, +) -> Result<(), Error> { + handler.send_outbound(dst.into()).await; + Ok(()) } /// Types necessary implement trait [`NatHolePunch`] on [`super::Handler`]. From 975376154fba9ba96e94482b8f5389c6f670d184 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 15:35:49 +0200 Subject: [PATCH 092/154] Simplify bind Co-authored-by: Divma <26765164+divagant-martian@users.noreply.github.com> --- src/handler/nat_hole_puncher.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index b2b21f824..68f1510a1 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -172,8 +172,7 @@ pub fn is_behind_nat( }; for _ in 0..retries { let rnd_port: u16 = rng.gen_range(unused_port_range.clone()); - let socket_addr: SocketAddr = format!("{}:{}", observed_ip, rnd_port).parse().unwrap(); - if UdpSocket::bind(socket_addr).is_ok() { + if UdpSocket::bind((observed_ip, rnd_port)).is_ok() { return false; } } From cae34a97a9c7cf83dbcb2743df6fcb9705a32bb4 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 16:27:20 +0200 Subject: [PATCH 093/154] Expose unused ports range parameter in config --- src/config.rs | 17 ++++++++- src/handler/mod.rs | 8 +++-- src/handler/nat_hole_puncher.rs | 62 +++++++++++++++++++++++++-------- 3 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/config.rs b/src/config.rs index a189a162b..edde65aac 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,7 +3,7 @@ use crate::{ Executor, PermitBanList, RateLimiter, RateLimiterBuilder, }; ///! A set of configuration parameters to tune the discovery protocol. -use std::time::Duration; +use std::{ops::RangeInclusive, time::Duration}; /// Configuration parameters that define the performance of the discovery network. #[derive(Clone)] @@ -105,6 +105,10 @@ pub struct Discv5Config { /// such peer in discovering their reachable socket via ip voting, and peers behind symmetric /// NAT. Default is no limit. Minimum is 1. pub unreachable_enr_limit: Option, + + /// The unused port range to try and bind to when testing if this node is behind NAT based on + /// observed address reported at runtime by peers. + pub unused_port_range: Option>, } impl Default for Discv5Config { @@ -145,6 +149,7 @@ impl Default for Discv5Config { ip_mode: IpMode::default(), executor: None, unreachable_enr_limit: None, + unused_port_range: None, } } } @@ -328,6 +333,16 @@ impl Discv5ConfigBuilder { self } + /// Sets the unused port range for testing if node is behind a NAT. Default is the range + /// covering user and dynamic ports. + pub fn unused_port_range( + &mut self, + unused_port_range: Option>, + ) -> &mut Self { + self.config.unused_port_range = unused_port_range; + self + } + pub fn build(&mut self) -> Discv5Config { // If an executor is not provided, assume a current tokio runtime is running. if self.config.executor.is_none() { diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 3cd70aa71..94291f6c4 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -269,8 +269,12 @@ impl Handler

{ // Attempt to bind to the socket before spinning up the send/recv tasks. let socket = Socket::new::

(socket_config).await?; - let nat_hole_puncher = - NatHolePunchUtils::new(listen_socket.port(), &enr.read(), config.ip_mode); + let nat_hole_puncher = NatHolePunchUtils::new( + listen_socket.port(), + &enr.read(), + config.ip_mode, + config.unused_port_range.clone(), + ); let sessions = Sessions::new( config.session_cache_capacity, diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index fc9a22a51..6759dbd9f 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -24,7 +24,7 @@ use tracing::{trace, warn}; /// The expected shortest lifetime in most NAT configurations of a punched hole in seconds. pub const DEFAULT_HOLE_PUNCH_LIFETIME: u64 = 20; /// The default number of ports to try before concluding that the local node is behind NAT. -pub const DEFAULT_PORT_BIND_TRIES: usize = 4; +pub const PORT_BIND_TRIES: usize = 4; /// Port range that is not impossible to bind to. pub const USER_AND_DYNAMIC_PORTS: RangeInclusive = 1025..=u16::MAX; @@ -200,7 +200,7 @@ pub async fn on_hole_punch_expired( Ok(()) } -/// Types necessary implement trait [`NatHolePunch`] on [`super::Handler`]. +/// Types necessary to implement nat hole punching for [`Handler`]. pub(crate) struct NatHolePunchUtils { /// Ip mode as set in config. pub ip_mode: IpMode, @@ -213,15 +213,23 @@ pub(crate) struct NatHolePunchUtils { /// Keeps track if this node needs to send a packet to a peer in order to keep a hole punched /// for it in its NAT. pub hole_punch_tracker: HashSetDelay, + /// Ports to trie to bind to check if this node is behind NAT. + pub unused_port_range: Option>, } impl NatHolePunchUtils { - pub(crate) fn new(listen_port: u16, local_enr: &Enr, ip_mode: IpMode) -> Self { + pub(crate) fn new( + listen_port: u16, + local_enr: &Enr, + ip_mode: IpMode, + unused_port_range: Option>, + ) -> Self { let mut nat_hole_puncher = NatHolePunchUtils { ip_mode, is_behind_nat: None, new_peer_latest_relay: Default::default(), hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), + unused_port_range, }; // Optimistically only test one advertised socket, ipv4 has precedence. If it is // reachable, assumption is made that also the other ip version socket is reachable. @@ -272,7 +280,7 @@ impl NatHolePunchUtils { let Some(ip) = observed_ip else { return; }; - self.is_behind_nat = Some(is_behind_nat(ip, None, None)); + self.is_behind_nat = Some(is_behind_nat(ip, &self.unused_port_range)); } } @@ -291,23 +299,15 @@ impl Stream for NatHolePunchUtils { /// Helper function to test if the local node is behind NAT based on the node's observed reachable /// socket. -pub fn is_behind_nat( - observed_ip: IpAddr, - unused_port_range: Option>, - max_retries: Option, -) -> bool { +pub fn is_behind_nat(observed_ip: IpAddr, unused_port_range: &Option>) -> bool { // If the node cannot bind to the observed address at any of some random ports, we // conclude it is behind NAT. let mut rng = rand::thread_rng(); let unused_port_range = match unused_port_range { Some(range) => range, - None => USER_AND_DYNAMIC_PORTS, - }; - let retries = match max_retries { - Some(max) => max, - None => DEFAULT_PORT_BIND_TRIES, + None => &USER_AND_DYNAMIC_PORTS, }; - for _ in 0..retries { + for _ in 0..PORT_BIND_TRIES { let rnd_port: u16 = rng.gen_range(unused_port_range.clone()); let socket_addr: SocketAddr = format!("{}:{}", observed_ip, rnd_port).parse().unwrap(); if UdpSocket::bind(socket_addr).is_ok() { @@ -316,3 +316,35 @@ pub fn is_behind_nat( } true } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_is_not_behind_nat() { + assert!(!is_behind_nat(IpAddr::from([127, 0, 0, 1]), &None)); + } + + #[test] + fn test_is_behind_nat() { + assert!(is_behind_nat(IpAddr::from([8, 8, 8, 8]), &None)); + } + + #[test] + fn test_is_not_behind_nat_ipv6() { + assert!(!is_behind_nat( + IpAddr::from([0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 1u16]), + &None, + )); + } + + #[test] + fn test_is_behind_nat_ipv6() { + // google's ipv6 + assert!(is_behind_nat( + IpAddr::from([2001, 4860, 4860, 0u16, 0u16, 0u16, 0u16, 0u16]), + &None, + )); + } +} From 2a1bf4537ebe36536d5439a5a48e4ac373936339 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 16:27:41 +0200 Subject: [PATCH 094/154] Fix typo Co-authored-by: Divma <26765164+divagant-martian@users.noreply.github.com> --- src/handler/nat_hole_puncher.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index 68f1510a1..8efba5a08 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -25,7 +25,7 @@ pub const USER_AND_DYNAMIC_PORTS: RangeInclusive = 1025..=u16::MAX; /// An error occurred whilst attempting to hole punch NAT. #[derive(Debug, Error)] pub enum Error { - #[error("NAT error, failed as initiator a hole punch attempt, {0}")] + #[error("NAT error, failed as initiator of a hole punch attempt, {0}")] Initiator(Discv5Error), #[error("NAT error, failed as relay of a hole punch attempt, {0}")] Relay(Discv5Error), From 5b750584f832d44bb2fc8049295797d5fee86a18 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 25 May 2023 17:20:39 +0200 Subject: [PATCH 095/154] Fix ci for ipv6 tests --- .github/workflows/build.yml | 2 +- src/handler/nat_hole_puncher.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 558918fd4..c174c9538 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ jobs: - name: Install protobuf compiler for the libp2p-core dependency uses: arduino/setup-protoc@v1 - name: Run tests in release - run: cargo test --all --release --all-features + run: cargo test --all --release --all-features -- --skip ipv6 # ipv6 tests don't run in github ci https://github.com/actions/runner-images/issues/668 check-rustdoc-links: name: Check rustdoc intra-doc links runs-on: ubuntu-latest diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index 9ef51f417..02746f695 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -330,6 +330,7 @@ mod test { assert!(is_behind_nat(IpAddr::from([8, 8, 8, 8]), &None)); } + // ipv6 tests don't run in github ci https://github.com/actions/runner-images/issues/668 #[test] fn test_is_not_behind_nat_ipv6() { assert!(!is_behind_nat( @@ -338,6 +339,7 @@ mod test { )); } + // ipv6 tests don't run in github ci https://github.com/actions/runner-images/issues/668 #[test] fn test_is_behind_nat_ipv6() { // google's ipv6 From 0f4d7b5e81123476db924edd2cebb48003f4fc7a Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 8 Jul 2023 12:07:36 +0200 Subject: [PATCH 096/154] Name consistently with specs Co-authored-by: Age Manning --- src/packet/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packet/mod.rs b/src/packet/mod.rs index 76b4de38a..9d5f03926 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -285,7 +285,7 @@ impl PacketKind { }) } 3 => { - // Decoding a notification packet + // Decoding a SessionMessage packet // This should only contain a 32 byte NodeId. if auth_data.len() != 32 { return Err(PacketError::InvalidAuthDataSize); From 789f3f059453e78e8fcbf56b27c35d275ac368a2 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 8 Jul 2023 12:09:40 +0200 Subject: [PATCH 097/154] Fix typo Co-authored-by: Age Manning --- src/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc.rs b/src/rpc.rs index d622eaa11..5fbbd49d0 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -344,7 +344,7 @@ mod tests { } #[test] - fn test_enocde_decode_relay_init() { + fn test_encode_decode_relay_init() { // generate a new enr key for the initiator let enr_key = CombinedKey::generate_secp256k1(); // construct the initiator's ENR From f46b964cd9384fae9a1f517d8226787f1a4570b9 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 21 Jun 2023 10:30:43 +0200 Subject: [PATCH 098/154] Only track outgoing packets if node is behind nat --- src/handler/mod.rs | 4 +++- src/handler/nat_hole_puncher.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 2b885b1bb..c6ebf3930 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1350,7 +1350,9 @@ impl Handler

{ if let Err(e) = self.socket.send.send(packet).await { warn!("Failed to send outbound packet {}", e) } - self.nat_hole_puncher.track(dst); + if Some(false) != self.nat_hole_puncher.is_behind_nat { + self.nat_hole_puncher.track(dst); + } } /// Check if any banned nodes have served their time and unban them. diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index 02746f695..973ec52f2 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -299,7 +299,7 @@ impl Stream for NatHolePunchUtils { /// Helper function to test if the local node is behind NAT based on the node's observed reachable /// socket. -pub fn is_behind_nat(observed_ip: IpAddr, unused_port_range: &Option>) -> bool { +fn is_behind_nat(observed_ip: IpAddr, unused_port_range: &Option>) -> bool { // If the node cannot bind to the observed address at any of some random ports, we // conclude it is behind NAT. let mut rng = rand::thread_rng(); From 1b0e0dd15c77b42d420311d26e303f216eb80b89 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 21 Jun 2023 10:45:22 +0200 Subject: [PATCH 099/154] Only process relay messages if we are behind a nat --- src/handler/mod.rs | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index c6ebf3930..5cd056252 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1040,19 +1040,33 @@ impl Handler

{ // Handle standard responses self.handle_response(node_address, response).await; } - Message::Notification(notif) => { - let res = match notif { - Notification::RelayInit(initr, tgt, timed_out_nonce) => { + Message::Notification(notif) => match notif { + Notification::RelayInit(initr, tgt, timed_out_nonce) => { + if let Err(e) = nat_hole_puncher::on_relay_init(self, initr, tgt, timed_out_nonce).await + { + warn!("failed handling notification to relay for {node_address}, {e}"); } - Notification::RelayMsg(initr, timed_out_nonce) => { - nat_hole_puncher::on_relay_msg(self, initr, timed_out_nonce).await + } + Notification::RelayMsg(initr, timed_out_nonce) => { + match self.nat_hole_puncher.is_behind_nat { + Some(false) => { + // initr may not be malicious and initiated a hole punch attempt when + // a request to this node timed out for another reason + debug!("peer {node_address} relayed a hole punch notification but we are not behind nat"); + } + _ => { + if let Err(e) = + nat_hole_puncher::on_relay_msg(self, initr, timed_out_nonce).await + { + warn!( + "failed handling notification relayed from {node_address}, {e}" + ); + } + } } - }; - if let Err(e) = res { - warn!("failed handling notification from {node_address}, {e}"); } - } + }, _ => { warn!( "Peer sent message type that shouldn't be sent in packet type Message, {}", From 4750490760931b3ba89afa44594f7187fd74a7b5 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 21 Jun 2023 11:10:07 +0200 Subject: [PATCH 100/154] Catch malicious relay init --- src/error.rs | 3 +++ src/handler/mod.rs | 13 ++++++++++++- src/handler/nat_hole_puncher.rs | 4 ++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index effe1df30..c6323dffb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -127,6 +127,9 @@ pub enum RequestError { InvalidMultiaddr(&'static str), /// Failure generating random numbers during request. EntropyFailure(&'static str), + /// Malicious peer tried to initiate nat hole punching for another peer. todo(emhane): this is + /// notification error. + MaliciousRelayInit, } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 5cd056252..f6f8001e9 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -274,6 +274,7 @@ impl Handler

{ &enr.read(), config.ip_mode, config.unused_port_range.clone(), + config.ban_duration, ); let sessions = Sessions::new( @@ -1042,7 +1043,17 @@ impl Handler

{ } Message::Notification(notif) => match notif { Notification::RelayInit(initr, tgt, timed_out_nonce) => { - if let Err(e) = + let initr_node_id = initr.node_id(); + if initr_node_id != node_address.node_id { + warn!("peer {node_address} tried to initiate hole punch attempt for another node {initr_node_id}, banning peer {node_address}"); + self.fail_session(&node_address, RequestError::MaliciousRelayInit, true) + .await; + let ban_timeout = self + .nat_hole_puncher + .ban_duration + .map(|v| Instant::now() + v); + PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); + } else if let Err(e) = nat_hole_puncher::on_relay_init(self, initr, tgt, timed_out_nonce).await { warn!("failed handling notification to relay for {node_address}, {e}"); diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index 973ec52f2..a4262d980 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -215,6 +215,8 @@ pub(crate) struct NatHolePunchUtils { pub hole_punch_tracker: HashSetDelay, /// Ports to trie to bind to check if this node is behind NAT. pub unused_port_range: Option>, + /// If the filter is enabled this sets the default timeout for bans enacted by the filter. + pub ban_duration: Option, } impl NatHolePunchUtils { @@ -223,6 +225,7 @@ impl NatHolePunchUtils { local_enr: &Enr, ip_mode: IpMode, unused_port_range: Option>, + ban_duration: Option, ) -> Self { let mut nat_hole_puncher = NatHolePunchUtils { ip_mode, @@ -230,6 +233,7 @@ impl NatHolePunchUtils { new_peer_latest_relay: Default::default(), hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), unused_port_range, + ban_duration, }; // Optimistically only test one advertised socket, ipv4 has precedence. If it is // reachable, assumption is made that also the other ip version socket is reachable. From 0477bdb3bd961abae1c2362debb2005eeefd3cd0 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 27 Nov 2023 12:00:58 +0100 Subject: [PATCH 101/154] Clear hole punch tracker set for remaining entries --- src/handler/nat_hole_puncher.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index a4262d980..6c1a53027 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -249,10 +249,7 @@ impl NatHolePunchUtils { (_, _, Some(ip6), port) => { nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip6.into()), port); } - (None, Some(port), _, _) => { - nat_hole_puncher.set_is_behind_nat(listen_port, None, Some(port)); - } - (_, _, None, Some(port)) => { + (None, Some(port), _, _) | (_, _, None, Some(port)) => { nat_hole_puncher.set_is_behind_nat(listen_port, None, Some(port)); } (None, None, None, None) => {} @@ -284,7 +281,15 @@ impl NatHolePunchUtils { let Some(ip) = observed_ip else { return; }; - self.is_behind_nat = Some(is_behind_nat(ip, &self.unused_port_range)); + + self.is_behind_nat = Some(match is_behind_nat(ip, &self.unused_port_range) { + true => true, + false => { + // node assume it is behind NAT until now + self.hole_punch_tracker.clear(); + false + } + }); } } From 8852d93bc29e4c9d315b23c8b67b4ee0324d7ae0 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 27 Nov 2023 12:01:37 +0100 Subject: [PATCH 102/154] Run lint --- src/handler/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index f6f8001e9..1f3d37461 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1017,8 +1017,8 @@ impl Handler

{ if let ResponseBody::Nodes { mut nodes, .. } = response.body { // Received the requested ENR let Some(enr) = nodes.pop() else { - return; - }; + return; + }; if self.verify_enr(&enr, &node_address) { // Notify the application // This can occur when we try to dial a node without an From f03662ac2deb25d80ff4ce2d6c1896183e3563c5 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 27 Nov 2023 14:58:51 +0100 Subject: [PATCH 103/154] Introduce bound on relay store --- Cargo.lock | 6 ------ Cargo.toml | 2 +- src/handler/mod.rs | 17 +++++++++-------- src/handler/nat_hole_puncher.rs | 14 +++++++++----- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e32002b09..740ea14f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -712,9 +712,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] [[package]] name = "hashlink" @@ -995,9 +992,6 @@ name = "lru" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" -dependencies = [ - "hashbrown 0.12.3", -] [[package]] name = "matchers" diff --git a/Cargo.toml b/Cargo.toml index b73a2de80..5be8e0bde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ aes = { version = "0.7.5", features = ["ctr"] } aes-gcm = "0.9.4" tracing = { version = "0.1.29", features = ["log"] } tracing-subscriber = { version = "0.3.3", features = ["env-filter"] } -lru = "0.7.1" +lru = {version = "0.7.1", default-features = false } hashlink = "0.7.0" delay_map = "0.3.0" more-asserts = "0.2.2" diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 1f3d37461..0837a511e 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -275,6 +275,7 @@ impl Handler

{ config.ip_mode, config.unused_port_range.clone(), config.ban_duration, + config.session_cache_capacity, ); let sessions = Sessions::new( @@ -475,8 +476,8 @@ impl Handler

{ trace!("Request timed out with {}", node_address); if let Some(relay) = self .nat_hole_puncher - .new_peer_latest_relay - .remove(&node_address.node_id) + .new_peer_latest_relay_cache + .pop(&node_address.node_id) { // The request might be timing out because the peer is behind a NAT. If we // have a relay to the peer, attempt NAT hole punching. @@ -864,8 +865,8 @@ impl Handler

{ .await; self.new_session(node_address.clone(), session); self.nat_hole_puncher - .new_peer_latest_relay - .remove(&node_address.node_id); + .new_peer_latest_relay_cache + .pop(&node_address.node_id); self.handle_message( node_address.clone(), message_nonce, @@ -1210,8 +1211,8 @@ impl Handler

{ }; if self.sessions.cache.peek(&new_peer_node_address).is_none() { self.nat_hole_puncher - .new_peer_latest_relay - .insert(node_id, node_address.clone()); + .new_peer_latest_relay_cache + .put(node_id, node_address.clone()); } } } @@ -1317,8 +1318,8 @@ impl Handler

{ } let node_address = request_call.contact().node_address(); self.nat_hole_puncher - .new_peer_latest_relay - .remove(&node_address.node_id); + .new_peer_latest_relay_cache + .pop(&node_address.node_id); self.fail_session(&node_address, error, remove_session) .await; } diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs index 6c1a53027..a8f40b492 100644 --- a/src/handler/nat_hole_puncher.rs +++ b/src/handler/nat_hole_puncher.rs @@ -8,9 +8,9 @@ use crate::{ use delay_map::HashSetDelay; use enr::NodeId; use futures::{Stream, StreamExt}; +use lru::LruCache; use rand::Rng; use std::{ - collections::HashMap, fmt::Debug, net::{IpAddr, SocketAddr, UdpSocket}, ops::RangeInclusive, @@ -19,7 +19,7 @@ use std::{ time::Duration, }; use thiserror::Error; -use tracing::{trace, warn}; +use tracing::{error, trace, warn}; /// The expected shortest lifetime in most NAT configurations of a punched hole in seconds. pub const DEFAULT_HOLE_PUNCH_LIFETIME: u64 = 20; @@ -208,8 +208,11 @@ pub(crate) struct NatHolePunchUtils { pub is_behind_nat: Option, /// The last peer to send us a new peer in a NODES response is stored as the new peer's /// potential relay until the first request to the new peer after its discovery is either - /// responded or failed. - pub new_peer_latest_relay: HashMap, + /// responded or failed. The cache will usually be emptied by successful or failed session + /// establishment, but for the edge case that a NODES response is returned for an ended query + /// and hence an attempt to establish a session with those nodes isn't initiated, a bound on + /// the relay cache is set equivalent to the Handler's `session_cache_capacity`. + pub new_peer_latest_relay_cache: LruCache, /// Keeps track if this node needs to send a packet to a peer in order to keep a hole punched /// for it in its NAT. pub hole_punch_tracker: HashSetDelay, @@ -226,11 +229,12 @@ impl NatHolePunchUtils { ip_mode: IpMode, unused_port_range: Option>, ban_duration: Option, + session_cache_capacity: usize, ) -> Self { let mut nat_hole_puncher = NatHolePunchUtils { ip_mode, is_behind_nat: None, - new_peer_latest_relay: Default::default(), + new_peer_latest_relay_cache: LruCache::new(session_cache_capacity), hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), unused_port_range, ban_duration, From b5c0766576ea9406a183b9f7bb20e50026b22b0e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 27 Nov 2023 18:14:08 +0100 Subject: [PATCH 104/154] Remove redundant parameter --- src/handler/mod.rs | 1 - src/handler/nat_hole_punch/error.rs | 14 +++ src/handler/nat_hole_punch/mod.rs | 53 ++++++++ src/handler/nat_hole_punch/utils.rs | 185 ++++++++++++++++++++++++++++ src/handler/sessions/session.rs | 5 +- 5 files changed, 254 insertions(+), 4 deletions(-) create mode 100644 src/handler/nat_hole_punch/error.rs create mode 100644 src/handler/nat_hole_punch/mod.rs create mode 100644 src/handler/nat_hole_punch/utils.rs diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 0837a511e..3f96b881c 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -841,7 +841,6 @@ impl Handler

{ match Session::establish_from_challenge( self.key.clone(), &self.node_id, - &node_address.node_id, challenge, id_nonce_sig, ephem_pubkey, diff --git a/src/handler/nat_hole_punch/error.rs b/src/handler/nat_hole_punch/error.rs new file mode 100644 index 000000000..58d48b522 --- /dev/null +++ b/src/handler/nat_hole_punch/error.rs @@ -0,0 +1,14 @@ +use thiserror::Error; + +use crate::Discv5Error; + +/// An error occurred whilst attempting to hole punch NAT. +#[derive(Debug, Error)] +pub enum Error { + #[error("NAT error, failed as initiator of a hole punch attempt, {0}")] + Initiator(Discv5Error), + #[error("NAT error, failed as relay of a hole punch attempt, {0}")] + Relay(Discv5Error), + #[error("NAT error, failed as target of a hole punch attempt, {0}")] + Target(Discv5Error), +} diff --git a/src/handler/nat_hole_punch/mod.rs b/src/handler/nat_hole_punch/mod.rs new file mode 100644 index 000000000..b2dd3f8ba --- /dev/null +++ b/src/handler/nat_hole_punch/mod.rs @@ -0,0 +1,53 @@ +use std::net::SocketAddr; + +use enr::NodeId; + +use crate::{node_info::NodeAddress, packet::MessageNonce, rpc::Notification, Enr}; + +mod error; +mod utils; + +pub use error::Error; +pub use utils::NatHolePunchUtils; + +#[async_trait::async_trait] +pub trait HolePunchNat { + /// A request times out. Should trigger the initiation of a hole punch attempt, given a + /// transitive route to the target exists. Sends a RELAYINIT notification to the given + /// relay. + async fn on_request_time_out( + &mut self, + relay: NodeAddress, + local_enr: Enr, // initiator-enr + timed_out_nonce: MessageNonce, + target_session_index: NodeAddress, + ) -> Result<(), Error>; + + /// A RelayInit notification is received over discv5 indicating this node is the relay. Should + /// trigger sending a RelayMsg to the target. + async fn on_relay_init( + &mut self, + initr: Enr, + tgt: NodeId, + timed_out_nonce: MessageNonce, + ) -> Result<(), Error>; + + /// A RelayMsg notification is received over discv5 indicating this node is the target. Should + /// trigger a WHOAREYOU to be sent to the initiator using the `nonce` in the RelayMsg. + async fn on_relay_msg( + &mut self, + initr: Enr, + timed_out_nonce: MessageNonce, + ) -> Result<(), Error>; + + /// Send a RELAYMSG notification. + async fn send_relay_msg_notif( + &mut self, + tgt_enr: Enr, + relay_msg_notif: Notification, + ) -> Result<(), Error>; + + /// A hole punched for a peer closes. Should trigger an empty packet to be sent to the + /// peer to keep it open. + async fn on_hole_punch_expired(&mut self, peer: SocketAddr) -> Result<(), Error>; +} diff --git a/src/handler/nat_hole_punch/utils.rs b/src/handler/nat_hole_punch/utils.rs new file mode 100644 index 000000000..8958b25d7 --- /dev/null +++ b/src/handler/nat_hole_punch/utils.rs @@ -0,0 +1,185 @@ +use std::{ + net::{IpAddr, SocketAddr, UdpSocket}, + ops::RangeInclusive, + pin::Pin, + task::{Context, Poll}, + time::Duration, +}; + +use delay_map::HashSetDelay; +use enr::NodeId; +use futures::{Stream, StreamExt}; +use lru::LruCache; +use rand::Rng; + +use crate::{node_info::NodeAddress, Enr, IpMode}; + +/// The expected shortest lifetime in most NAT configurations of a punched hole in seconds. +pub const DEFAULT_HOLE_PUNCH_LIFETIME: u64 = 20; +/// The default number of ports to try before concluding that the local node is behind NAT. +pub const PORT_BIND_TRIES: usize = 4; +/// Port range that is not impossible to bind to. +pub const USER_AND_DYNAMIC_PORTS: RangeInclusive = 1025..=u16::MAX; + +/// Aggregates types necessary to implement nat hole punching for [`Handler`]. +pub struct NatHolePunchUtils { + /// Ip mode as set in config. + pub ip_mode: IpMode, + /// This node has been observed to be behind a NAT. + pub is_behind_nat: Option, + /// The last peer to send us a new peer in a NODES response is stored as the new peer's + /// potential relay until the first request to the new peer after its discovery is either + /// responded or failed. The cache will usually be emptied by successful or failed session + /// establishment, but for the edge case that a NODES response is returned for an ended query + /// and hence an attempt to establish a session with those nodes isn't initiated, a bound on + /// the relay cache is set equivalent to the Handler's `session_cache_capacity`. + pub new_peer_latest_relay_cache: LruCache, + /// Keeps track if this node needs to send a packet to a peer in order to keep a hole punched + /// for it in its NAT. + pub hole_punch_tracker: HashSetDelay, + /// Ports to trie to bind to check if this node is behind NAT. + pub unused_port_range: Option>, + /// If the filter is enabled this sets the default timeout for bans enacted by the filter. + pub ban_duration: Option, +} + +impl NatHolePunchUtils { + pub(crate) fn new( + listen_port: u16, + local_enr: &Enr, + ip_mode: IpMode, + unused_port_range: Option>, + ban_duration: Option, + session_cache_capacity: usize, + ) -> Self { + let mut nat_hole_puncher = NatHolePunchUtils { + ip_mode, + is_behind_nat: None, + new_peer_latest_relay_cache: LruCache::new(session_cache_capacity), + hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), + unused_port_range, + ban_duration, + }; + // Optimistically only test one advertised socket, ipv4 has precedence. If it is + // reachable, assumption is made that also the other ip version socket is reachable. + match ( + local_enr.ip4(), + local_enr.udp4(), + local_enr.ip6(), + local_enr.udp6(), + ) { + (Some(ip), port, _, _) => { + nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip.into()), port); + } + (_, _, Some(ip6), port) => { + nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip6.into()), port); + } + (None, Some(port), _, _) | (_, _, None, Some(port)) => { + nat_hole_puncher.set_is_behind_nat(listen_port, None, Some(port)); + } + (None, None, None, None) => {} + } + nat_hole_puncher + } + + pub(crate) fn track(&mut self, peer_socket: SocketAddr) { + if self.is_behind_nat == Some(false) { + return; + } + self.hole_punch_tracker.insert(peer_socket); + } + + /// Called when a new observed address is reported at start up or after a + /// [`crate::Discv5Event::SocketUpdated`]. + pub(crate) fn set_is_behind_nat( + &mut self, + listen_port: u16, + observed_ip: Option, + observed_port: Option, + ) { + if Some(listen_port) != observed_port { + self.is_behind_nat = Some(true); + return; + } + // Without and observed IP it is too early to conclude if the local node is behind a NAT, + // return. + let Some(ip) = observed_ip else { + return; + }; + + self.is_behind_nat = Some(match is_behind_nat(ip, &self.unused_port_range) { + true => true, + false => { + // node assume it is behind NAT until now + self.hole_punch_tracker.clear(); + false + } + }); + } +} + +impl Stream for NatHolePunchUtils { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // Until ip voting is done and an observed public address is finalised, all nodes act as + // if they are behind a NAT. + if self.is_behind_nat == Some(false) || self.hole_punch_tracker.is_empty() { + return Poll::Pending; + } + self.hole_punch_tracker.poll_next_unpin(cx) + } +} + +/// Helper function to test if the local node is behind NAT based on the node's observed reachable +/// socket. +fn is_behind_nat(observed_ip: IpAddr, unused_port_range: &Option>) -> bool { + // If the node cannot bind to the observed address at any of some random ports, we + // conclude it is behind NAT. + let mut rng = rand::thread_rng(); + let unused_port_range = match unused_port_range { + Some(range) => range, + None => &USER_AND_DYNAMIC_PORTS, + }; + for _ in 0..PORT_BIND_TRIES { + let rnd_port: u16 = rng.gen_range(unused_port_range.clone()); + if UdpSocket::bind((observed_ip, rnd_port)).is_ok() { + return false; + } + } + true +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_is_not_behind_nat() { + assert!(!is_behind_nat(IpAddr::from([127, 0, 0, 1]), &None)); + } + + #[test] + fn test_is_behind_nat() { + assert!(is_behind_nat(IpAddr::from([8, 8, 8, 8]), &None)); + } + + // ipv6 tests don't run in github ci https://github.com/actions/runner-images/issues/668 + #[test] + fn test_is_not_behind_nat_ipv6() { + assert!(!is_behind_nat( + IpAddr::from([0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 1u16]), + &None, + )); + } + + // ipv6 tests don't run in github ci https://github.com/actions/runner-images/issues/668 + #[test] + fn test_is_behind_nat_ipv6() { + // google's ipv6 + assert!(is_behind_nat( + IpAddr::from([2001, 4860, 4860, 0u16, 0u16, 0u16, 0u16, 0u16]), + &None, + )); + } +} diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index e1b4842d7..d0c789910 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -199,7 +199,6 @@ impl Session { pub(crate) fn establish_from_challenge( local_key: Arc>, local_id: &NodeId, - remote_id: &NodeId, challenge: Challenge, id_nonce_sig: &[u8], ephem_pubkey: &[u8], @@ -223,7 +222,7 @@ impl Session { (None, None) => { warn!( "Peer did not respond with their ENR. Session could not be established. Node: {}", - remote_id + node_address.node_id ); return Err(Discv5Error::SessionNotEstablished); } @@ -255,7 +254,7 @@ impl Session { let (decryption_key, encryption_key) = crypto::derive_keys_from_pubkey( &local_key.read(), local_id, - remote_id, + &node_address.node_id, &challenge.data, ephem_pubkey, )?; From 2c6023583753fb1ee64d8c24f176af86d16cc3df Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 27 Nov 2023 18:20:25 +0100 Subject: [PATCH 105/154] Keep session but drop packet that could be spoofed --- src/handler/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 3f96b881c..665abb6e6 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -982,9 +982,7 @@ impl Handler

{ // likely the node sending this message has dropped their session. Since // this is a session message that assumes an established session, we do // not reply with a WHOAREYOU to this random packet. This means we drop - // the current session and the packet. - self.fail_session(&node_address, RequestError::InvalidRemotePacket, true) - .await; + // the packet. warn!( "Dropping message that should have been part of a session. Error: {}", e From 7cde68c645acbfa39eb622ea1c9de9b154d33490 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 27 Nov 2023 18:32:03 +0100 Subject: [PATCH 106/154] Add debug info --- src/handler/mod.rs | 5 +++-- src/rpc.rs | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 665abb6e6..b3fb4ea18 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1078,8 +1078,9 @@ impl Handler

{ }, _ => { warn!( - "Peer sent message type that shouldn't be sent in packet type Message, {}", - node_address + "Peer sent message type {} that shouldn't be sent in packet type `Message`, {}", + message.msg_type(), + node_address, ); } } diff --git a/src/rpc.rs b/src/rpc.rs index 5fbbd49d0..38b462611 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -97,6 +97,14 @@ impl Message { } } } + + pub fn msg_type(&self) -> String { + match self { + Self::Notification(n) => format!("notification type {}", n.msg_type()), + Self::Request(r) => format!("request type {}", r.msg_type()), + Self::Response(r) => format!("response type {}", r.msg_type()), + } + } } #[cfg(test)] From 32321a709a731464e02e4ed3bebe9b513f7bddd0 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 27 Nov 2023 18:38:02 +0100 Subject: [PATCH 107/154] Revert layout of hole punch code to use trait --- Cargo.lock | 2255 ----------------------------- Cargo.toml | 1 + src/handler/mod.rs | 169 ++- src/handler/nat_hole_punch/mod.rs | 2 +- src/handler/nat_hole_puncher.rs | 364 ----- 5 files changed, 160 insertions(+), 2631 deletions(-) delete mode 100644 Cargo.lock delete mode 100644 src/handler/nat_hole_puncher.rs diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 740ea14f7..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,2255 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aead" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" -dependencies = [ - "generic-array", -] - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", - "ctr", - "opaque-debug", -] - -[[package]] -name = "aes-gcm" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.9", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "asn1_der" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] - -[[package]] -name = "clap" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "atty", - "bitflags", - "clap_derive", - "clap_lex", - "indexmap", - "once_cell", - "strsim", - "termcolor", - "textwrap", -] - -[[package]] -name = "clap_derive" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "const-oid" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "cpufeatures" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "ctr" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "delay_map" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4355c25cbf99edcb6b4a0e906f6bdc6956eda149e84455bea49696429b2f8e8" -dependencies = [ - "futures", - "tokio-util", -] - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", - "subtle", -] - -[[package]] -name = "discv5" -version = "0.2.2" -dependencies = [ - "aes", - "aes-gcm", - "arrayvec", - "clap", - "delay_map", - "derive_more", - "enr", - "fnv", - "futures", - "hashlink", - "hex", - "hkdf", - "lazy_static", - "libp2p-core", - "lru", - "more-asserts", - "parking_lot 0.11.2", - "quickcheck", - "rand 0.7.3", - "rand 0.8.5", - "rand_core 0.6.4", - "rand_xorshift", - "rlp", - "smallvec", - "socket2", - "thiserror", - "tokio", - "tracing", - "tracing-subscriber", - "uint", - "zeroize", -] - -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand 0.7.3", - "serde", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "digest 0.10.7", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "enr" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "492a7e5fc2504d5fdce8e124d3e263b244a68b283cac67a69eda0cd43e0aebad" -dependencies = [ - "base64", - "bs58", - "bytes", - "ed25519-dalek", - "hex", - "k256", - "log", - "rand 0.8.5", - "rlp", - "serde", - "sha3", - "zeroize", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", - "num_cpus", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "ghash" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashlink" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" -dependencies = [ - "hashbrown 0.11.2", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "hmac-drbg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" -dependencies = [ - "digest 0.9.0", - "generic-array", - "hmac 0.8.1", -] - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sha2 0.10.6", -] - -[[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.144" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" - -[[package]] -name = "libp2p-core" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1fff5bd889c82a0aec668f2045edd066f559d4e5c40354e5a4c77ac00caac38" -dependencies = [ - "asn1_der", - "bs58", - "ed25519-dalek", - "either", - "fnv", - "futures", - "futures-timer", - "instant", - "lazy_static", - "libsecp256k1", - "log", - "multiaddr", - "multihash", - "multistream-select", - "p256", - "parking_lot 0.12.1", - "pin-project", - "prost", - "prost-build", - "rand 0.8.5", - "rw-stream-sink", - "sha2 0.10.6", - "smallvec", - "thiserror", - "unsigned-varint", - "void", - "zeroize", -] - -[[package]] -name = "libsecp256k1" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" -dependencies = [ - "arrayref", - "base64", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde", - "sha2 0.9.9", - "typenum", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "lru" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "mio" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", -] - -[[package]] -name = "more-asserts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" - -[[package]] -name = "multiaddr" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" -dependencies = [ - "arrayref", - "bs58", - "byteorder", - "data-encoding", - "multihash", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", - "url", -] - -[[package]] -name = "multihash" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" -dependencies = [ - "core2", - "digest 0.10.7", - "multihash-derive", - "sha2 0.10.6", - "unsigned-varint", -] - -[[package]] -name = "multihash-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" -dependencies = [ - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "multistream-select" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "363a84be6453a70e63513660f4894ef815daf88e3356bffcda9ca27d810ce83b" -dependencies = [ - "bytes", - "futures", - "log", - "pin-project", - "smallvec", - "unsigned-varint", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "p256" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" -dependencies = [ - "ecdsa", - "elliptic-curve", - "sha2 0.10.6", -] - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "petgraph" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "pin-project" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "polyval" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "proc-macro-crate" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" -dependencies = [ - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 1.0.109", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost", -] - -[[package]] -name = "quickcheck" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" -dependencies = [ - "env_logger", - "log", - "rand 0.7.3", - "rand_core 0.5.1", -] - -[[package]] -name = "quote" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.9", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.2", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint", - "hmac 0.12.1", - "zeroize", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rustc-hex", -] - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustix" -version = "0.37.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rw-stream-sink" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" -dependencies = [ - "futures", - "pin-project", - "static_assertions", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "serde" -version = "1.0.163" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.163" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - -[[package]] -name = "tempfile" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - -[[package]] -name = "thiserror" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" -dependencies = [ - "autocfg", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "tokio-util" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "slab", - "tokio", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "tracing-core" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "unsigned-varint" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "zeroize" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] diff --git a/Cargo.toml b/Cargo.toml index 5be8e0bde..5cd4085ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ delay_map = "0.3.0" more-asserts = "0.2.2" thiserror = "1.0.40" derive_more = { version = "0.99.17", default-features = false, features = ["from", "display"] } +async-trait = "0.1.74" [dev-dependencies] rand_07 = { package = "rand", version = "0.7" } diff --git a/src/handler/mod.rs b/src/handler/mod.rs index b3fb4ea18..a927077ea 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -57,7 +57,7 @@ use tokio::sync::{mpsc, oneshot}; use tracing::{debug, error, trace, warn}; mod active_requests; -mod nat_hole_puncher; +mod nat_hole_punch; mod request_call; mod sessions; mod tests; @@ -67,7 +67,7 @@ pub use crate::node_info::{NodeAddress, NodeContact}; pub use sessions::MIN_SESSIONS_UNREACHABLE_ENR; use active_requests::ActiveRequests; -use nat_hole_puncher::NatHolePunchUtils; +use nat_hole_punch::{Error as NatHolePunchError, HolePunchNat, NatHolePunchUtils}; use request_call::RequestCall; use sessions::{Session, Sessions}; @@ -334,7 +334,7 @@ impl Handler

{ HandlerIn::Response(dst, response) => self.send_response(dst, *response).await, HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge(wru_ref, enr).await, HandlerIn::HolePunchEnr(tgt_enr, relay_msg_notif) => { - if let Err(e) = nat_hole_puncher::send_relay_msg_notif(self, tgt_enr, relay_msg_notif).await { + if let Err(e) = self.send_relay_msg_notif(tgt_enr, relay_msg_notif).await { warn!("Failed to relay. Error: {}", e); } } @@ -368,7 +368,7 @@ impl Handler

{ self.send_next_request(node_address).await; } Some(Ok(peer_socket)) = self.nat_hole_puncher.next() => { - if let Err(e) = nat_hole_puncher::on_hole_punch_expired(self, peer_socket).await { + if let Err(e) = self.on_hole_punch_expired(peer_socket).await { warn!("Failed to keep hole punched for peer, error: {}", e); } } @@ -485,7 +485,8 @@ impl Handler

{ trace!("Trying to hole punch target {target} with relay {relay}"); let local_enr = self.enr.read().clone(); let nonce = request_call.packet().header.message_nonce; - match nat_hole_puncher::on_request_time_out(self, relay, local_enr, nonce, target) + match self + .on_request_time_out(relay, local_enr, nonce, target) .await { Err(e) => { @@ -1051,9 +1052,7 @@ impl Handler

{ .ban_duration .map(|v| Instant::now() + v); PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); - } else if let Err(e) = - nat_hole_puncher::on_relay_init(self, initr, tgt, timed_out_nonce).await - { + } else if let Err(e) = self.on_relay_init(initr, tgt, timed_out_nonce).await { warn!("failed handling notification to relay for {node_address}, {e}"); } } @@ -1065,9 +1064,7 @@ impl Handler

{ debug!("peer {node_address} relayed a hole punch notification but we are not behind nat"); } _ => { - if let Err(e) = - nat_hole_puncher::on_relay_msg(self, initr, timed_out_nonce).await - { + if let Err(e) = self.on_relay_msg(initr, timed_out_nonce).await { warn!( "failed handling notification relayed from {node_address}, {e}" ); @@ -1409,3 +1406,153 @@ impl Handler

{ } } } + +#[async_trait::async_trait] +impl HolePunchNat for Handler

{ + async fn on_request_time_out( + &mut self, + relay: NodeAddress, + local_enr: Enr, // initiator-enr + timed_out_nonce: MessageNonce, + target_session_index: NodeAddress, + ) -> Result<(), NatHolePunchError> { + // Another hole punch process with this target may have just completed. + if self.sessions.cache.get(&target_session_index).is_some() { + return Ok(()); + } + if let Some(session) = self.sessions.cache.get_mut(&relay) { + let relay_init_notif = + Notification::RelayInit(local_enr, target_session_index.node_id, timed_out_nonce); + trace!( + "Sending notif to relay {}. relay init: {}", + relay.node_id, + relay_init_notif, + ); + // Encrypt the message and send + let packet = + match session.encrypt_notification::

(self.node_id, &relay_init_notif.encode()) { + Ok(packet) => packet, + Err(e) => { + return Err(NatHolePunchError::Initiator(e)); + } + }; + self.send(relay, packet).await; + } else { + // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays + // within the time out of the udp entrypoint for the target peer in the initiator's + // router, set by the original timed out FINDNODE request from the initiator, as the + // initiator may also be behind a NAT. + warn!( + "Session is not established. Dropping relay notification for relay: {}", + relay.node_id + ); + } + Ok(()) + } + + async fn on_relay_init( + &mut self, + initr: Enr, + tgt: NodeId, + timed_out_nonce: MessageNonce, + ) -> Result<(), NatHolePunchError> { + // Assemble the notification for the target + let relay_msg_notif = Notification::RelayMsg(initr, timed_out_nonce); + + // Check for target peer in our kbuckets otherwise drop notification. + if let Err(e) = self + .service_send + .send(HandlerOut::FindHolePunchEnr(tgt, relay_msg_notif)) + .await + { + return Err(NatHolePunchError::Relay(e.into())); + } + Ok(()) + } + + async fn on_relay_msg( + &mut self, + initr: Enr, + timed_out_nonce: MessageNonce, + ) -> Result<(), NatHolePunchError> { + let initiator_node_address = + match NodeContact::try_from_enr(initr, self.nat_hole_puncher.ip_mode) { + Ok(contact) => contact.node_address(), + Err(e) => return Err(NatHolePunchError::Target(e.into())), + }; + + // A session may already have been established. + if self.sessions.cache.get(&initiator_node_address).is_some() { + trace!( + "Session already established with initiator: {}", + initiator_node_address + ); + return Ok(()); + } + // Possibly, an attempt to punch this hole, using another relay, is in progress. + if self + .active_challenges + .get(&initiator_node_address) + .is_some() + { + trace!( + "WHOAREYOU packet already sent to initiator: {}", + initiator_node_address + ); + return Ok(()); + } + // If not hole punch attempts are in progress, spawn a WHOAREYOU event to punch a hole in + // our NAT for initiator. + let whoareyou_ref = WhoAreYouRef(initiator_node_address, timed_out_nonce); + if let Err(e) = self + .service_send + .send(HandlerOut::WhoAreYou(whoareyou_ref)) + .await + { + return Err(NatHolePunchError::Target(e.into())); + } + Ok(()) + } + + async fn send_relay_msg_notif( + &mut self, + tgt_enr: Enr, + relay_msg_notif: Notification, + ) -> Result<(), NatHolePunchError> { + let tgt_node_address = + match NodeContact::try_from_enr(tgt_enr, self.nat_hole_puncher.ip_mode) { + Ok(contact) => contact.node_address(), + Err(e) => return Err(NatHolePunchError::Relay(e.into())), + }; + if let Some(session) = self.sessions.cache.get_mut(&tgt_node_address) { + trace!( + "Sending notif to target {}. relay msg: {}", + tgt_node_address.node_id, + relay_msg_notif, + ); + // Encrypt the notification and send + let packet = + match session.encrypt_notification::

(self.node_id, &relay_msg_notif.encode()) { + Ok(packet) => packet, + Err(e) => { + return Err(NatHolePunchError::Relay(e)); + } + }; + self.send(tgt_node_address, packet).await; + Ok(()) + } else { + // Either the session is being established or has expired. We simply drop the + // notification in this case to ensure hole punch round-trip time stays within the + // time out of the udp entrypoint for the target peer in the initiator's NAT, set by + // the original timed out FINDNODE request from the initiator, as the initiator may + // also be behind a NAT. + Err(NatHolePunchError::Relay(Discv5Error::SessionNotEstablished)) + } + } + + #[inline] + async fn on_hole_punch_expired(&mut self, peer: SocketAddr) -> Result<(), NatHolePunchError> { + self.send_outbound(peer.into()).await; + Ok(()) + } +} diff --git a/src/handler/nat_hole_punch/mod.rs b/src/handler/nat_hole_punch/mod.rs index b2dd3f8ba..9f3e963fa 100644 --- a/src/handler/nat_hole_punch/mod.rs +++ b/src/handler/nat_hole_punch/mod.rs @@ -13,7 +13,7 @@ pub use utils::NatHolePunchUtils; #[async_trait::async_trait] pub trait HolePunchNat { /// A request times out. Should trigger the initiation of a hole punch attempt, given a - /// transitive route to the target exists. Sends a RELAYINIT notification to the given + /// transitive route to the target exists. Sends a RELAYINIT notification to the given /// relay. async fn on_request_time_out( &mut self, diff --git a/src/handler/nat_hole_puncher.rs b/src/handler/nat_hole_puncher.rs deleted file mode 100644 index a8f40b492..000000000 --- a/src/handler/nat_hole_puncher.rs +++ /dev/null @@ -1,364 +0,0 @@ -use crate::{ - handler::{Handler, HandlerOut, WhoAreYouRef}, - node_info::{NodeAddress, NodeContact}, - packet::MessageNonce, - rpc::{Notification, Payload}, - Discv5Error, Enr, IpMode, ProtocolIdentity, -}; -use delay_map::HashSetDelay; -use enr::NodeId; -use futures::{Stream, StreamExt}; -use lru::LruCache; -use rand::Rng; -use std::{ - fmt::Debug, - net::{IpAddr, SocketAddr, UdpSocket}, - ops::RangeInclusive, - pin::Pin, - task::{Context, Poll}, - time::Duration, -}; -use thiserror::Error; -use tracing::{error, trace, warn}; - -/// The expected shortest lifetime in most NAT configurations of a punched hole in seconds. -pub const DEFAULT_HOLE_PUNCH_LIFETIME: u64 = 20; -/// The default number of ports to try before concluding that the local node is behind NAT. -pub const PORT_BIND_TRIES: usize = 4; -/// Port range that is not impossible to bind to. -pub const USER_AND_DYNAMIC_PORTS: RangeInclusive = 1025..=u16::MAX; - -/// An error occurred whilst attempting to hole punch NAT. -#[derive(Debug, Error)] -pub enum Error { - #[error("NAT error, failed as initiator of a hole punch attempt, {0}")] - Initiator(Discv5Error), - #[error("NAT error, failed as relay of a hole punch attempt, {0}")] - Relay(Discv5Error), - #[error("NAT error, failed as target of a hole punch attempt, {0}")] - Target(Discv5Error), -} - -/// A request times out. Should trigger the initiation of a hole punch attempt, given a -/// transitive route to the target exists. -pub async fn on_request_time_out( - handler: &mut Handler

, - relay: NodeAddress, - local_enr: Enr, // initiator-enr - timed_out_nonce: MessageNonce, - target_session_index: NodeAddress, -) -> Result<(), Error> { - // Another hole punch process with this target may have just completed. - if handler.sessions.cache.get(&target_session_index).is_some() { - return Ok(()); - } - if let Some(session) = handler.sessions.cache.get_mut(&relay) { - let relay_init_notif = - Notification::RelayInit(local_enr, target_session_index.node_id, timed_out_nonce); - trace!( - "Sending notif to relay {}. relay init: {}", - relay.node_id, - relay_init_notif, - ); - // Encrypt the message and send - let packet = - match session.encrypt_notification::

(handler.node_id, &relay_init_notif.encode()) { - Ok(packet) => packet, - Err(e) => { - return Err(Error::Initiator(e)); - } - }; - handler.send(relay, packet).await; - } else { - // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays - // within the time out of the udp entrypoint for the target peer in the initiator's - // router, set by the original timed out FINDNODE request from the initiator, as the - // initiator may also be behind a NAT. - warn!( - "Session is not established. Dropping relay notification for relay: {}", - relay.node_id - ); - } - Ok(()) -} - -/// A RelayInit notification is received over discv5 indicating this node is the relay. Should -/// trigger sending a RelayMsg to the target. -pub async fn on_relay_init( - handler: &mut Handler

, - initr: Enr, - tgt: NodeId, - timed_out_nonce: MessageNonce, -) -> Result<(), Error> { - // Assemble the notification for the target - let relay_msg_notif = Notification::RelayMsg(initr, timed_out_nonce); - - // Check for target peer in our kbuckets otherwise drop notification. - if let Err(e) = handler - .service_send - .send(HandlerOut::FindHolePunchEnr(tgt, relay_msg_notif)) - .await - { - return Err(Error::Relay(e.into())); - } - Ok(()) -} - -/// A RelayMsg notification is received over discv5 indicating this node is the target. Should -/// trigger a WHOAREYOU to be sent to the initiator using the `nonce` in the RelayMsg. -pub async fn on_relay_msg( - handler: &mut Handler

, - initr: Enr, - timed_out_nonce: MessageNonce, -) -> Result<(), Error> { - let initiator_node_address = - match NodeContact::try_from_enr(initr, handler.nat_hole_puncher.ip_mode) { - Ok(contact) => contact.node_address(), - Err(e) => return Err(Error::Target(e.into())), - }; - - // A session may already have been established. - if handler - .sessions - .cache - .get(&initiator_node_address) - .is_some() - { - trace!( - "Session already established with initiator: {}", - initiator_node_address - ); - return Ok(()); - } - // Possibly, an attempt to punch this hole, using another relay, is in progress. - if handler - .active_challenges - .get(&initiator_node_address) - .is_some() - { - trace!( - "WHOAREYOU packet already sent to initiator: {}", - initiator_node_address - ); - return Ok(()); - } - // If not hole punch attempts are in progress, spawn a WHOAREYOU event to punch a hole in - // our NAT for initiator. - let whoareyou_ref = WhoAreYouRef(initiator_node_address, timed_out_nonce); - if let Err(e) = handler - .service_send - .send(HandlerOut::WhoAreYou(whoareyou_ref)) - .await - { - return Err(Error::Target(e.into())); - } - Ok(()) -} - -pub async fn send_relay_msg_notif( - handler: &mut Handler

, - tgt_enr: Enr, - relay_msg_notif: Notification, -) -> Result<(), Error> { - let tgt_node_address = - match NodeContact::try_from_enr(tgt_enr, handler.nat_hole_puncher.ip_mode) { - Ok(contact) => contact.node_address(), - Err(e) => return Err(Error::Relay(e.into())), - }; - if let Some(session) = handler.sessions.cache.get_mut(&tgt_node_address) { - trace!( - "Sending notif to target {}. relay msg: {}", - tgt_node_address.node_id, - relay_msg_notif, - ); - // Encrypt the notification and send - let packet = - match session.encrypt_notification::

(handler.node_id, &relay_msg_notif.encode()) { - Ok(packet) => packet, - Err(e) => { - return Err(Error::Relay(e)); - } - }; - handler.send(tgt_node_address, packet).await; - Ok(()) - } else { - // Either the session is being established or has expired. We simply drop the - // notification in this case to ensure hole punch round-trip time stays within the - // time out of the udp entrypoint for the target peer in the initiator's NAT, set by - // the original timed out FINDNODE request from the initiator, as the initiator may - // also be behind a NAT. - Err(Error::Relay(Discv5Error::SessionNotEstablished)) - } -} - -/// A punched hole closes. Should trigger an empty packet to be sent to the peer. -pub async fn on_hole_punch_expired( - handler: &mut Handler

, - dst: SocketAddr, -) -> Result<(), Error> { - handler.send_outbound(dst.into()).await; - Ok(()) -} - -/// Types necessary to implement nat hole punching for [`Handler`]. -pub(crate) struct NatHolePunchUtils { - /// Ip mode as set in config. - pub ip_mode: IpMode, - /// This node has been observed to be behind a NAT. - pub is_behind_nat: Option, - /// The last peer to send us a new peer in a NODES response is stored as the new peer's - /// potential relay until the first request to the new peer after its discovery is either - /// responded or failed. The cache will usually be emptied by successful or failed session - /// establishment, but for the edge case that a NODES response is returned for an ended query - /// and hence an attempt to establish a session with those nodes isn't initiated, a bound on - /// the relay cache is set equivalent to the Handler's `session_cache_capacity`. - pub new_peer_latest_relay_cache: LruCache, - /// Keeps track if this node needs to send a packet to a peer in order to keep a hole punched - /// for it in its NAT. - pub hole_punch_tracker: HashSetDelay, - /// Ports to trie to bind to check if this node is behind NAT. - pub unused_port_range: Option>, - /// If the filter is enabled this sets the default timeout for bans enacted by the filter. - pub ban_duration: Option, -} - -impl NatHolePunchUtils { - pub(crate) fn new( - listen_port: u16, - local_enr: &Enr, - ip_mode: IpMode, - unused_port_range: Option>, - ban_duration: Option, - session_cache_capacity: usize, - ) -> Self { - let mut nat_hole_puncher = NatHolePunchUtils { - ip_mode, - is_behind_nat: None, - new_peer_latest_relay_cache: LruCache::new(session_cache_capacity), - hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), - unused_port_range, - ban_duration, - }; - // Optimistically only test one advertised socket, ipv4 has precedence. If it is - // reachable, assumption is made that also the other ip version socket is reachable. - match ( - local_enr.ip4(), - local_enr.udp4(), - local_enr.ip6(), - local_enr.udp6(), - ) { - (Some(ip), port, _, _) => { - nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip.into()), port); - } - (_, _, Some(ip6), port) => { - nat_hole_puncher.set_is_behind_nat(listen_port, Some(ip6.into()), port); - } - (None, Some(port), _, _) | (_, _, None, Some(port)) => { - nat_hole_puncher.set_is_behind_nat(listen_port, None, Some(port)); - } - (None, None, None, None) => {} - } - nat_hole_puncher - } - - pub(crate) fn track(&mut self, peer_socket: SocketAddr) { - if self.is_behind_nat == Some(false) { - return; - } - self.hole_punch_tracker.insert(peer_socket); - } - - /// Called when a new observed address is reported at start up or after a - /// [`crate::Discv5Event::SocketUpdated`]. - pub(crate) fn set_is_behind_nat( - &mut self, - listen_port: u16, - observed_ip: Option, - observed_port: Option, - ) { - if Some(listen_port) != observed_port { - self.is_behind_nat = Some(true); - return; - } - // Without and observed IP it is too early to conclude if the local node is behind a NAT, - // return. - let Some(ip) = observed_ip else { - return; - }; - - self.is_behind_nat = Some(match is_behind_nat(ip, &self.unused_port_range) { - true => true, - false => { - // node assume it is behind NAT until now - self.hole_punch_tracker.clear(); - false - } - }); - } -} - -impl Stream for NatHolePunchUtils { - type Item = Result; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - // Until ip voting is done and an observed public address is finalised, all nodes act as - // if they are behind a NAT. - if self.is_behind_nat == Some(false) || self.hole_punch_tracker.is_empty() { - return Poll::Pending; - } - self.hole_punch_tracker.poll_next_unpin(cx) - } -} - -/// Helper function to test if the local node is behind NAT based on the node's observed reachable -/// socket. -fn is_behind_nat(observed_ip: IpAddr, unused_port_range: &Option>) -> bool { - // If the node cannot bind to the observed address at any of some random ports, we - // conclude it is behind NAT. - let mut rng = rand::thread_rng(); - let unused_port_range = match unused_port_range { - Some(range) => range, - None => &USER_AND_DYNAMIC_PORTS, - }; - for _ in 0..PORT_BIND_TRIES { - let rnd_port: u16 = rng.gen_range(unused_port_range.clone()); - if UdpSocket::bind((observed_ip, rnd_port)).is_ok() { - return false; - } - } - true -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_is_not_behind_nat() { - assert!(!is_behind_nat(IpAddr::from([127, 0, 0, 1]), &None)); - } - - #[test] - fn test_is_behind_nat() { - assert!(is_behind_nat(IpAddr::from([8, 8, 8, 8]), &None)); - } - - // ipv6 tests don't run in github ci https://github.com/actions/runner-images/issues/668 - #[test] - fn test_is_not_behind_nat_ipv6() { - assert!(!is_behind_nat( - IpAddr::from([0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 1u16]), - &None, - )); - } - - // ipv6 tests don't run in github ci https://github.com/actions/runner-images/issues/668 - #[test] - fn test_is_behind_nat_ipv6() { - // google's ipv6 - assert!(is_behind_nat( - IpAddr::from([2001, 4860, 4860, 0u16, 0u16, 0u16, 0u16, 0u16]), - &None, - )); - } -} From abe42257539f59173d9498a7692784f157a1a3f2 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 15:31:41 +0100 Subject: [PATCH 108/154] Fix backwards compatibility of packet type Message --- src/handler/mod.rs | 95 ++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index a927077ea..3829c6cfe 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1003,43 +1003,7 @@ impl Handler

{ }; match message { - Message::Response(response) => { - // Sessions could be awaiting an ENR response. Check if this response matches - // these - let Some(request_id) = session.awaiting_enr.as_ref() else { - // Handle standard responses - self.handle_response(node_address, response).await; - return; - }; - if &response.id == request_id { - session.awaiting_enr = None; - if let ResponseBody::Nodes { mut nodes, .. } = response.body { - // Received the requested ENR - let Some(enr) = nodes.pop() else { - return; - }; - if self.verify_enr(&enr, &node_address) { - // Notify the application - // This can occur when we try to dial a node without an - // ENR. In this case we have attempted to establish the - // connection, so this is an outgoing connection. - self.new_connection( - enr, - node_address.socket_addr, - ConnectionDirection::Outgoing, - ) - .await; - return; - } - } - debug!("Session failed invalid ENR response"); - self.fail_session(&node_address, RequestError::InvalidRemoteEnr, true) - .await; - return; - } - // Handle standard responses - self.handle_response(node_address, response).await; - } + Message::Response(response) => self.handle_response(node_address, response).await, Message::Notification(notif) => match notif { Notification::RelayInit(initr, tgt, timed_out_nonce) => { let initr_node_id = initr.node_id(); @@ -1073,9 +1037,9 @@ impl Handler

{ } } }, - _ => { + Message::Request(_) => { warn!( - "Peer sent message type {} that shouldn't be sent in packet type `Message`, {}", + "Peer sent message type {} that shouldn't be sent in packet type `Session Message`, {}", message.msg_type(), node_address, ); @@ -1146,9 +1110,15 @@ impl Handler

{ warn!("Failed to report request to application {}", e) } } - _ => { + Message::Response(response) => { + // Accept response in Message packet for backwards compatibility + warn!("Received a response in a `Message` packet, should be sent in a `SessionMessage`"); + self.handle_response(node_address, response).await + } + Message::Notification(_) => { warn!( - "Peer sent message type that shouldn't be sent in packet type Message, {}", + "Peer sent message type {} that shouldn't be sent in packet type `Message`, {}", + message.msg_type(), node_address ); } @@ -1175,6 +1145,49 @@ impl Handler

{ /// Handles a response to a request. Re-inserts the request call if the response is a multiple /// Nodes response. async fn handle_response(&mut self, node_address: NodeAddress, response: Response) { + // Sessions could be awaiting an ENR response. Check if this response matches + // this + // check if we have an available session + let Some(session) = self.sessions.cache.get_mut(&node_address) else { + warn!( + "Dropping response. Error: {}, {}", + Discv5Error::SessionNotEstablished, + node_address + ); + return; + }; + + if let Some(request_id) = session.awaiting_enr.as_ref() { + if &response.id == request_id { + session.awaiting_enr = None; + if let ResponseBody::Nodes { mut nodes, .. } = response.body { + // Received the requested ENR + let Some(enr) = nodes.pop() else { + return; + }; + if self.verify_enr(&enr, &node_address) { + // Notify the application + // This can occur when we try to dial a node without an + // ENR. In this case we have attempted to establish the + // connection, so this is an outgoing connection. + self.new_connection( + enr, + node_address.socket_addr, + ConnectionDirection::Outgoing, + ) + .await; + return; + } + } + debug!("Session failed invalid ENR response"); + self.fail_session(&node_address, RequestError::InvalidRemoteEnr, true) + .await; + return; + } + } + + // Handle standard responses + // Find a matching request, if any if let Some(mut request_call) = self.active_requests.remove(&node_address) { let id = match request_call.id() { From fbb5a000c24115015c35754d56a89785f06192e5 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 15:32:51 +0100 Subject: [PATCH 109/154] Remove duplicate guard --- src/handler/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 3829c6cfe..b38c4cc26 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1384,9 +1384,7 @@ impl Handler

{ if let Err(e) = self.socket.send.send(packet).await { warn!("Failed to send outbound packet {}", e) } - if Some(false) != self.nat_hole_puncher.is_behind_nat { - self.nat_hole_puncher.track(dst); - } + self.nat_hole_puncher.track(dst); } /// Check if any banned nodes have served their time and unban them. From b18e463d7c6cda8c47a1b99193baf35dcbacb10e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 16:51:14 +0100 Subject: [PATCH 110/154] Use lru time cach for tracking sent packages --- Cargo.toml | 2 +- src/handler/mod.rs | 6 ++-- src/handler/nat_hole_punch/utils.rs | 44 +++++++++++++++++++++++------ 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5cd4085ba..d8af3279b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ hashlink = "0.7.0" delay_map = "0.3.0" more-asserts = "0.2.2" thiserror = "1.0.40" -derive_more = { version = "0.99.17", default-features = false, features = ["from", "display"] } +derive_more = { version = "0.99.17", default-features = false, features = ["from", "display", "deref", "deref_mut"] } async-trait = "0.1.74" [dev-dependencies] diff --git a/src/handler/mod.rs b/src/handler/mod.rs index b38c4cc26..a9a735b51 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -367,7 +367,7 @@ impl Handler

{ // challenge. We process them here self.send_next_request(node_address).await; } - Some(Ok(peer_socket)) = self.nat_hole_puncher.next() => { + Some(peer_socket) = self.nat_hole_puncher.next() => { if let Err(e) = self.on_hole_punch_expired(peer_socket).await { warn!("Failed to keep hole punched for peer, error: {}", e); } @@ -1345,9 +1345,7 @@ impl Handler

{ .active_sessions .store(self.sessions.cache.len(), Ordering::Relaxed); // stop keeping hole punched for peer - self.nat_hole_puncher - .hole_punch_tracker - .remove(&node_address.socket_addr); + self.nat_hole_puncher.untrack(&node_address.socket_addr); } if let Some(to_remove) = self.pending_requests.remove(node_address) { for PendingRequest { request_id, .. } in to_remove { diff --git a/src/handler/nat_hole_punch/utils.rs b/src/handler/nat_hole_punch/utils.rs index 8958b25d7..18e938559 100644 --- a/src/handler/nat_hole_punch/utils.rs +++ b/src/handler/nat_hole_punch/utils.rs @@ -6,13 +6,13 @@ use std::{ time::Duration, }; -use delay_map::HashSetDelay; +use derive_more::{Deref, DerefMut}; use enr::NodeId; -use futures::{Stream, StreamExt}; +use futures::{channel::mpsc, Stream, StreamExt}; use lru::LruCache; use rand::Rng; -use crate::{node_info::NodeAddress, Enr, IpMode}; +use crate::{lru_time_cache::LruTimeCache, node_info::NodeAddress, Enr, IpMode}; /// The expected shortest lifetime in most NAT configurations of a punched hole in seconds. pub const DEFAULT_HOLE_PUNCH_LIFETIME: u64 = 20; @@ -36,7 +36,7 @@ pub struct NatHolePunchUtils { pub new_peer_latest_relay_cache: LruCache, /// Keeps track if this node needs to send a packet to a peer in order to keep a hole punched /// for it in its NAT. - pub hole_punch_tracker: HashSetDelay, + pub hole_punch_tracker: NatHolePunchTracker, /// Ports to trie to bind to check if this node is behind NAT. pub unused_port_range: Option>, /// If the filter is enabled this sets the default timeout for bans enacted by the filter. @@ -56,7 +56,7 @@ impl NatHolePunchUtils { ip_mode, is_behind_nat: None, new_peer_latest_relay_cache: LruCache::new(session_cache_capacity), - hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), + hole_punch_tracker: NatHolePunchTracker::new(session_cache_capacity), unused_port_range, ban_duration, }; @@ -86,7 +86,11 @@ impl NatHolePunchUtils { if self.is_behind_nat == Some(false) { return; } - self.hole_punch_tracker.insert(peer_socket); + self.hole_punch_tracker.insert(peer_socket, ()); + } + + pub fn untrack(&mut self, peer_socket: &SocketAddr) { + _ = self.hole_punch_tracker.remove(peer_socket) } /// Called when a new observed address is reported at start up or after a @@ -119,15 +123,37 @@ impl NatHolePunchUtils { } impl Stream for NatHolePunchUtils { - type Item = Result; + type Item = SocketAddr; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { // Until ip voting is done and an observed public address is finalised, all nodes act as // if they are behind a NAT. - if self.is_behind_nat == Some(false) || self.hole_punch_tracker.is_empty() { + if self.is_behind_nat == Some(false) || self.hole_punch_tracker.len() == 0 { return Poll::Pending; } - self.hole_punch_tracker.poll_next_unpin(cx) + self.hole_punch_tracker.expired_entries.poll_next_unpin(cx) + } +} + +#[derive(Deref, DerefMut)] +struct NatHolePunchTracker { + #[deref] + #[deref_mut] + cache: LruTimeCache, + expired_entries: mpsc::Receiver, +} + +impl NatHolePunchTracker { + fn new(session_cache_capacity: usize) -> Self { + let (tx, rx) = futures::channel::mpsc::channel::(session_cache_capacity); + Self { + cache: LruTimeCache::new( + Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME), + Some(session_cache_capacity), + Some(tx), + ), + expired_entries: rx, + } } } From 9241d032c7622aa7e05cbf9be9a627a4c6123613 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 16:59:04 +0100 Subject: [PATCH 111/154] Adjust visibility, handler not alone-standing crate --- src/handler/nat_hole_punch/utils.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/handler/nat_hole_punch/utils.rs b/src/handler/nat_hole_punch/utils.rs index 18e938559..28e12899a 100644 --- a/src/handler/nat_hole_punch/utils.rs +++ b/src/handler/nat_hole_punch/utils.rs @@ -36,7 +36,7 @@ pub struct NatHolePunchUtils { pub new_peer_latest_relay_cache: LruCache, /// Keeps track if this node needs to send a packet to a peer in order to keep a hole punched /// for it in its NAT. - pub hole_punch_tracker: NatHolePunchTracker, + hole_punch_tracker: NatHolePunchTracker, /// Ports to trie to bind to check if this node is behind NAT. pub unused_port_range: Option>, /// If the filter is enabled this sets the default timeout for bans enacted by the filter. @@ -44,7 +44,7 @@ pub struct NatHolePunchUtils { } impl NatHolePunchUtils { - pub(crate) fn new( + pub fn new( listen_port: u16, local_enr: &Enr, ip_mode: IpMode, @@ -82,7 +82,7 @@ impl NatHolePunchUtils { nat_hole_puncher } - pub(crate) fn track(&mut self, peer_socket: SocketAddr) { + pub fn track(&mut self, peer_socket: SocketAddr) { if self.is_behind_nat == Some(false) { return; } @@ -95,7 +95,7 @@ impl NatHolePunchUtils { /// Called when a new observed address is reported at start up or after a /// [`crate::Discv5Event::SocketUpdated`]. - pub(crate) fn set_is_behind_nat( + pub fn set_is_behind_nat( &mut self, listen_port: u16, observed_ip: Option, From 55833245099cb81f8550d507f5a0645591b602d2 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 17:00:07 +0100 Subject: [PATCH 112/154] Add signature for using session limiter on cache insert --- src/handler/sessions/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/handler/sessions/mod.rs b/src/handler/sessions/mod.rs index abf842efc..2d2ec99aa 100644 --- a/src/handler/sessions/mod.rs +++ b/src/handler/sessions/mod.rs @@ -34,6 +34,10 @@ impl Sessions { limiter, } } + + pub fn insert(key: NodeAddress, value: Session) { + todo!() + } } #[cfg(test)] From 535ea0a987b96289b63545cdd1b01d8165505022 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 17:02:27 +0100 Subject: [PATCH 113/154] Only send expired entries on channel --- src/lru_time_cache.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/lru_time_cache.rs b/src/lru_time_cache.rs index ae2a72370..36e76116e 100644 --- a/src/lru_time_cache.rs +++ b/src/lru_time_cache.rs @@ -92,13 +92,7 @@ impl LruTimeCache { /// Removes a key-value pair from the cache, returning the value at the key if the key /// was previously in the map. pub fn remove(&mut self, key: &K) -> Option { - let v = self.map.remove(key).map(|v| v.0); - if let Some(ref mut tx) = self.tx { - if let Err(e) = tx.try_send(key.clone()) { - warn!("failed to notify of remove, {}", e); - } - } - v + self.map.remove(key).map(|v| v.0) } /// Removes expired items from the cache. From 58bbb0ccba5747001c26edadc41c9c9195363e1e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 17:14:04 +0100 Subject: [PATCH 114/154] Remove duplicate code --- src/handler/mod.rs | 45 +++++++++++++------------ src/handler/sessions/session.rs | 60 ++++++++++----------------------- 2 files changed, 41 insertions(+), 64 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index a9a735b51..ac3403885 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -594,13 +594,14 @@ impl Handler

{ // Check for an established session if let Some(session) = self.sessions.cache.get_mut(&node_address) { // Encrypt the message and send - let packet = match session.encrypt_notification::

(self.node_id, &response.encode()) { - Ok(packet) => packet, - Err(e) => { - warn!("Could not encrypt response: {:?}", e); - return; - } - }; + let packet = + match session.encrypt_session_message::

(self.node_id, &response.encode()) { + Ok(packet) => packet, + Err(e) => { + warn!("Could not encrypt response: {:?}", e); + return; + } + }; self.send(node_address, packet).await; } else { // Either the session is being established or has expired. We simply drop the @@ -1438,13 +1439,14 @@ impl HolePunchNat for Handler

{ relay_init_notif, ); // Encrypt the message and send - let packet = - match session.encrypt_notification::

(self.node_id, &relay_init_notif.encode()) { - Ok(packet) => packet, - Err(e) => { - return Err(NatHolePunchError::Initiator(e)); - } - }; + let packet = match session + .encrypt_session_message::

(self.node_id, &relay_init_notif.encode()) + { + Ok(packet) => packet, + Err(e) => { + return Err(NatHolePunchError::Initiator(e)); + } + }; self.send(relay, packet).await; } else { // Drop hole punch attempt with this relay, to ensure hole punch round-trip time stays @@ -1540,13 +1542,14 @@ impl HolePunchNat for Handler

{ relay_msg_notif, ); // Encrypt the notification and send - let packet = - match session.encrypt_notification::

(self.node_id, &relay_msg_notif.encode()) { - Ok(packet) => packet, - Err(e) => { - return Err(NatHolePunchError::Relay(e)); - } - }; + let packet = match session + .encrypt_session_message::

(self.node_id, &relay_msg_notif.encode()) + { + Ok(packet) => packet, + Err(e) => { + return Err(NatHolePunchError::Relay(e)); + } + }; self.send(tgt_node_address, packet).await; Ok(()) } else { diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index d0c789910..6f16e97a1 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -6,7 +6,7 @@ use crate::{ ChallengeData, MessageNonce, Packet, PacketHeader, PacketKind, ProtocolIdentity, MESSAGE_NONCE_LENGTH, }, - rpc::RequestId, + rpc::{Message, RequestId}, Discv5Error, Enr, }; @@ -72,56 +72,30 @@ impl Session { self.awaiting_enr = new_session.awaiting_enr; } - /// Uses the current `Session` to encrypt a message. Encrypt packets with the current session - /// key if we are awaiting a response from AuthMessage. - pub(crate) fn encrypt_message( + /// Uses the current `Session` to encrypt a `SessionMessage`. + pub(crate) fn encrypt_session_message( &mut self, src_id: NodeId, message: &[u8], ) -> Result { - self.counter += 1; - - // If the message nonce length is ever set below 4 bytes this will explode. The packet - // size constants shouldn't be modified. - const _: () = assert!(MESSAGE_NONCE_LENGTH > 4); - - let random_nonce: [u8; MESSAGE_NONCE_LENGTH - 4] = rand::random(); - let mut message_nonce: MessageNonce = [0u8; MESSAGE_NONCE_LENGTH]; - message_nonce[..4].copy_from_slice(&self.counter.to_be_bytes()); - message_nonce[4..].copy_from_slice(&random_nonce); - - // the authenticated data is the IV concatenated with the packet header - let iv: u128 = rand::random(); - let header = PacketHeader { - message_nonce, - kind: PacketKind::Message { src_id }, - }; - - let mut authenticated_data = iv.to_be_bytes().to_vec(); - authenticated_data.extend_from_slice(&header.encode::

()); - - let cipher = crypto::encrypt_message( - &self.keys.encryption_key, - message_nonce, - message, - &authenticated_data, - )?; - - // construct a packet from the header and the cipher text - Ok(Packet { - iv, - header, - message: cipher, - }) + self.encrypt::

(message, PacketKind::SessionMessage { src_id }) } - /// Uses the current `Session` to encrypt a notification. Encrypt packets with the current - /// session key if we are awaiting a response from AuthMessage. Same as `encrypt_message` - /// apart form the the [`PacketHeader`]. - pub(crate) fn encrypt_notification( + /// Uses the current `Session` to encrypt a `Message`. + pub(crate) fn encrypt_message( &mut self, src_id: NodeId, message: &[u8], + ) -> Result { + self.encrypt::

(message, PacketKind::Message { src_id }) + } + + /// Encrypts packets with the current session key if we are awaiting a response from + /// AuthMessage. + fn encrypt( + &mut self, + message: &[u8], + packet_kind: PacketKind, ) -> Result { self.counter += 1; @@ -138,7 +112,7 @@ impl Session { let iv: u128 = rand::random(); let header = PacketHeader { message_nonce, - kind: PacketKind::SessionMessage { src_id }, + kind: packet_kind, }; let mut authenticated_data = iv.to_be_bytes().to_vec(); From bf72841cb22beee991c693e607f14f5793e6d955 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 17:47:45 +0100 Subject: [PATCH 115/154] Move assertion on constant outside of function --- src/handler/sessions/session.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index 6f16e97a1..1c55cb483 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -10,6 +10,10 @@ use crate::{ Discv5Error, Enr, }; +// If the message nonce length is ever set below 4 bytes this will explode. The packet +// size constants shouldn't be modified. +const _: () = assert!(MESSAGE_NONCE_LENGTH > 4); + use enr::{CombinedKey, NodeId}; use parking_lot::RwLock; use std::sync::Arc; @@ -99,10 +103,6 @@ impl Session { ) -> Result { self.counter += 1; - // If the message nonce length is ever set below 4 bytes this will explode. The packet - // size constants shouldn't be modified. - const _: () = assert!(MESSAGE_NONCE_LENGTH > 4); - let random_nonce: [u8; MESSAGE_NONCE_LENGTH - 4] = rand::random(); let mut message_nonce: MessageNonce = [0u8; MESSAGE_NONCE_LENGTH]; message_nonce[..4].copy_from_slice(&self.counter.to_be_bytes()); From 4038f350987f12f47b9d36baf1e28f1dbe864c9d Mon Sep 17 00:00:00 2001 From: Diva M Date: Wed, 24 May 2023 14:55:47 -0500 Subject: [PATCH 116/154] no unreachables in session enr --- src/handler/sessions/session.rs | 73 ++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index 1c55cb483..ffb234190 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -182,17 +182,25 @@ impl Session { ) -> Result<(Session, Enr), Discv5Error> { // check and verify a potential ENR update + let Challenge { data, remote_enr } = challenge; + // Avoid cloning an ENR - let session_enr = match (enr_record, challenge.remote_enr.as_ref()) { + let session_enr = match (enr_record, remote_enr) { (Some(new_enr), Some(known_enr)) => { if new_enr.seq() > known_enr.seq() { - MostRecentEnr::Handshake(new_enr) + MostRecentEnr::Handshake { + enr: new_enr, + challenge_enr: Some(known_enr), + } } else { - MostRecentEnr::Challenge(known_enr) + MostRecentEnr::Challenge { enr: known_enr } } } - (Some(new_enr), None) => MostRecentEnr::Handshake(new_enr), - (None, Some(known_enr)) => MostRecentEnr::Challenge(known_enr), + (Some(new_enr), None) => MostRecentEnr::Handshake { + enr: new_enr, + challenge_enr: None, + }, + (None, Some(known_enr)) => MostRecentEnr::Challenge { enr: known_enr }, (None, None) => { warn!( "Peer did not respond with their ENR. Session could not be established. Node: {}", @@ -214,10 +222,14 @@ impl Session { if !crypto::verify_authentication_nonce( &remote_public_key, ephem_pubkey, - &challenge.data, + &data, local_id, id_nonce_sig, ) { + let challenge = Challenge { + data, + remote_enr: session_enr.to_challenge_enr(), + }; return Err(Discv5Error::InvalidChallengeSignature(challenge)); } @@ -229,7 +241,7 @@ impl Session { &local_key.read(), local_id, &node_address.node_id, - &challenge.data, + &data, ephem_pubkey, )?; @@ -238,17 +250,7 @@ impl Session { decryption_key, }; - let session_enr = match session_enr { - MostRecentEnr::Challenge(_) => { - let Challenge { - data: _, - remote_enr, - } = challenge; - remote_enr.expect("unreachable") - } - MostRecentEnr::Handshake(enr) => enr, - }; - + let session_enr = session_enr.to_most_recent_enr(); Ok((Session::new(keys), session_enr)) } @@ -306,16 +308,37 @@ impl Session { } } -enum MostRecentEnr<'a> { - Challenge(&'a Enr), - Handshake(Enr), +enum MostRecentEnr { + Challenge { + enr: Enr, + }, + Handshake { + enr: Enr, + challenge_enr: Option, + }, } -impl<'a> MostRecentEnr<'a> { +impl MostRecentEnr { fn enr(&self) -> &Enr { - match *self { - Self::Challenge(enr) => enr, - Self::Handshake(ref enr) => enr, + match self { + Self::Challenge { enr } => enr, + Self::Handshake { enr, .. } => enr, + } + } + + fn to_most_recent_enr(self) -> Enr { + match self { + MostRecentEnr::Challenge { enr } => enr, + MostRecentEnr::Handshake { enr, .. } => enr, + } + } + fn to_challenge_enr(self) -> Option { + match self { + MostRecentEnr::Challenge { enr } => Some(enr), + MostRecentEnr::Handshake { + enr: _, + challenge_enr, + } => challenge_enr, } } } From 34e6a4cbf9748f5092262b8eb3b1b167be8aefdc Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 17:56:11 +0100 Subject: [PATCH 117/154] Avoid breaking change to lru time cache api --- src/handler/nat_hole_punch/utils.rs | 4 +-- src/handler/sessions/mod.rs | 18 +++++++------ src/lru_time_cache.rs | 42 +++++++++++++++++++---------- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/handler/nat_hole_punch/utils.rs b/src/handler/nat_hole_punch/utils.rs index 28e12899a..6581bba65 100644 --- a/src/handler/nat_hole_punch/utils.rs +++ b/src/handler/nat_hole_punch/utils.rs @@ -147,10 +147,10 @@ impl NatHolePunchTracker { fn new(session_cache_capacity: usize) -> Self { let (tx, rx) = futures::channel::mpsc::channel::(session_cache_capacity); Self { - cache: LruTimeCache::new( + cache: LruTimeCache::new_with_expiry_feedback( Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME), Some(session_cache_capacity), - Some(tx), + tx, ), expired_entries: rx, } diff --git a/src/handler/sessions/mod.rs b/src/handler/sessions/mod.rs index 2d2ec99aa..81ea5b608 100644 --- a/src/handler/sessions/mod.rs +++ b/src/handler/sessions/mod.rs @@ -20,19 +20,21 @@ impl Sessions { entry_ttl: Duration, unreachable_enr_limit: Option, ) -> Self { - let (tx, limiter) = match unreachable_enr_limit { + let (cache, limiter) = match unreachable_enr_limit { Some(limit) => { let (tx, rx) = futures::channel::mpsc::channel::(cache_capacity); let limiter = SessionLimiter::new(rx, limit); - (Some(tx), Some(limiter)) + let cache = + LruTimeCache::new_with_expiry_feedback(entry_ttl, Some(cache_capacity), tx); + (cache, Some(limiter)) + } + None => { + let cache = LruTimeCache::new(entry_ttl, Some(cache_capacity)); + (cache, None) } - None => (None, None), }; - let sessions = LruTimeCache::new(entry_ttl, Some(cache_capacity), tx); - Sessions { - cache: sessions, - limiter, - } + + Sessions { cache, limiter } } pub fn insert(key: NodeAddress, value: Session) { diff --git a/src/lru_time_cache.rs b/src/lru_time_cache.rs index 36e76116e..482bb4f78 100644 --- a/src/lru_time_cache.rs +++ b/src/lru_time_cache.rs @@ -17,10 +17,24 @@ pub struct LruTimeCache { } impl LruTimeCache { - pub fn new( + pub fn new(ttl: Duration, capacity: Option) -> LruTimeCache { + let capacity = if let Some(cap) = capacity { + cap + } else { + usize::MAX + }; + LruTimeCache { + map: LinkedHashMap::new(), + ttl, + capacity, + tx: None, + } + } + + pub fn new_with_expiry_feedback( ttl: Duration, capacity: Option, - tx: Option>, + tx: mpsc::Sender, ) -> LruTimeCache { let capacity = if let Some(cap) = capacity { cap @@ -31,7 +45,7 @@ impl LruTimeCache { map: LinkedHashMap::new(), ttl, capacity, - tx, + tx: Some(tx), } } @@ -131,7 +145,7 @@ mod tests { #[test] fn insert() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), None, None); + let mut cache = LruTimeCache::new(Duration::from_secs(10), None); cache.insert(1, 10); cache.insert(2, 20); @@ -144,7 +158,7 @@ mod tests { #[test] fn capacity() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2), None); + let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2)); cache.insert(1, 10); cache.insert(2, 20); @@ -158,7 +172,7 @@ mod tests { #[test] fn get() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2), None); + let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2)); cache.insert(1, 10); cache.insert(2, 20); @@ -173,7 +187,7 @@ mod tests { #[test] fn get_mut() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), None, None); + let mut cache = LruTimeCache::new(Duration::from_secs(10), None); cache.insert(1, 10); let v = cache.get_mut(&1).expect("should have value"); @@ -184,7 +198,7 @@ mod tests { #[test] fn peek() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2), None); + let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2)); cache.insert(1, 10); cache.insert(2, 20); @@ -198,7 +212,7 @@ mod tests { #[test] fn len() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), None, None); + let mut cache = LruTimeCache::new(Duration::from_secs(10), None); assert_eq!(0, cache.len()); @@ -210,7 +224,7 @@ mod tests { #[test] fn remove() { - let mut cache = LruTimeCache::new(Duration::from_secs(10), None, None); + let mut cache = LruTimeCache::new(Duration::from_secs(10), None); cache.insert(1, 10); assert_eq!(Some(10), cache.remove(&1)); @@ -226,7 +240,7 @@ mod tests { #[test] fn get() { - let mut cache = LruTimeCache::new(TTL, None, None); + let mut cache = LruTimeCache::new(TTL, None); cache.insert(1, 10); assert_eq!(Some(&10), cache.get(&1)); @@ -236,7 +250,7 @@ mod tests { #[test] fn peek() { - let mut cache = LruTimeCache::new(TTL, None, None); + let mut cache = LruTimeCache::new(TTL, None); cache.insert(1, 10); assert_eq!(Some(&10), cache.peek(&1)); @@ -246,7 +260,7 @@ mod tests { #[test] fn len() { - let mut cache = LruTimeCache::new(TTL, None, None); + let mut cache = LruTimeCache::new(TTL, None); cache.insert(1, 10); assert_eq!(1, cache.len()); @@ -256,7 +270,7 @@ mod tests { #[test] fn ttl() { - let mut cache = LruTimeCache::new(TTL, None, None); + let mut cache = LruTimeCache::new(TTL, None); cache.insert(1, 10); sleep(TTL / 4); cache.insert(2, 20); From ac7fe9fbb4e390b0113ac2b1b54e29d5020728c9 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 18:18:20 +0100 Subject: [PATCH 118/154] Change name 'session index' to name of session index type --- src/handler/mod.rs | 6 +- src/handler/nat_hole_punch/mod.rs | 2 +- src/handler/session.rs | 275 ++++++++++++++++++++++++++++++ src/handler/sessions/limiter.rs | 5 +- 4 files changed, 282 insertions(+), 6 deletions(-) create mode 100644 src/handler/session.rs diff --git a/src/handler/mod.rs b/src/handler/mod.rs index ac3403885..34ad45727 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1424,15 +1424,15 @@ impl HolePunchNat for Handler

{ relay: NodeAddress, local_enr: Enr, // initiator-enr timed_out_nonce: MessageNonce, - target_session_index: NodeAddress, + target_node_address: NodeAddress, ) -> Result<(), NatHolePunchError> { // Another hole punch process with this target may have just completed. - if self.sessions.cache.get(&target_session_index).is_some() { + if self.sessions.cache.get(&target_node_address).is_some() { return Ok(()); } if let Some(session) = self.sessions.cache.get_mut(&relay) { let relay_init_notif = - Notification::RelayInit(local_enr, target_session_index.node_id, timed_out_nonce); + Notification::RelayInit(local_enr, target_node_address.node_id, timed_out_nonce); trace!( "Sending notif to relay {}. relay init: {}", relay.node_id, diff --git a/src/handler/nat_hole_punch/mod.rs b/src/handler/nat_hole_punch/mod.rs index 9f3e963fa..40d0dee8c 100644 --- a/src/handler/nat_hole_punch/mod.rs +++ b/src/handler/nat_hole_punch/mod.rs @@ -20,7 +20,7 @@ pub trait HolePunchNat { relay: NodeAddress, local_enr: Enr, // initiator-enr timed_out_nonce: MessageNonce, - target_session_index: NodeAddress, + target_node_address: NodeAddress, ) -> Result<(), Error>; /// A RelayInit notification is received over discv5 indicating this node is the relay. Should diff --git a/src/handler/session.rs b/src/handler/session.rs new file mode 100644 index 000000000..9c1f96a8f --- /dev/null +++ b/src/handler/session.rs @@ -0,0 +1,275 @@ +use super::*; +use crate::{ + node_info::NodeContact, + packet::{ + ChallengeData, Packet, PacketHeader, PacketKind, ProtocolIdentity, MESSAGE_NONCE_LENGTH, + }, +}; +use enr::{CombinedKey, NodeId}; +use zeroize::Zeroize; + +#[derive(Zeroize, PartialEq)] +pub(crate) struct Keys { + /// The encryption key. + encryption_key: [u8; 16], + /// The decryption key. + decryption_key: [u8; 16], +} + +/// A Session containing the encryption/decryption keys. These are kept individually for a given +/// node. +pub(crate) struct Session { + /// The current keys used to encrypt/decrypt messages. + keys: Keys, + /// If a new handshake is being established, the older keys are maintained as race + /// conditions in the handshake can give different views of which keys are canon. + /// The key that worked to decrypt our last message (or are freshly established) exist in + /// `keys` and previous keys are optionally stored in `old_keys`. We attempt to decrypt + /// messages with `keys` before optionally trying `old_keys`. + old_keys: Option, + /// If we contacted this node without an ENR, i.e. via a multiaddr, during the session + /// establishment we request the nodes ENR. Once the ENR is received and verified, this session + /// becomes established. + /// + /// This field holds the request_id associated with the ENR request. + pub awaiting_enr: Option, + /// Number of messages sent. Used to ensure the nonce used in message encryption is always + /// unique. + counter: u32, +} + +impl Session { + pub fn new(keys: Keys) -> Self { + Session { + keys, + old_keys: None, + awaiting_enr: None, + counter: 0, + } + } + + /// A new session has been established. Update this session based on the new session. + pub fn update(&mut self, new_session: Session) { + // Optimistically assume the new keys are canonical. + self.old_keys = Some(std::mem::replace(&mut self.keys, new_session.keys)); + self.awaiting_enr = new_session.awaiting_enr; + } + + /// Uses the current `Session` to encrypt a message. Encrypt packets with the current session + /// key if we are awaiting a response from AuthMessage. + pub(crate) fn encrypt_message( + &mut self, + src_id: NodeId, + message: &[u8], + ) -> Result { + self.counter += 1; + + // If the message nonce length is ever set below 4 bytes this will explode. The packet + // size constants shouldn't be modified. + let random_nonce: [u8; MESSAGE_NONCE_LENGTH - 4] = rand::random(); + let mut message_nonce: MessageNonce = [0u8; MESSAGE_NONCE_LENGTH]; + message_nonce[..4].copy_from_slice(&self.counter.to_be_bytes()); + message_nonce[4..].copy_from_slice(&random_nonce); + + // the authenticated data is the IV concatenated with the packet header + let iv: u128 = rand::random(); + let header = PacketHeader { + message_nonce, + kind: PacketKind::Message { src_id }, + }; + + let mut authenticated_data = iv.to_be_bytes().to_vec(); + authenticated_data.extend_from_slice(&header.encode::

()); + + let cipher = crypto::encrypt_message( + &self.keys.encryption_key, + message_nonce, + message, + &authenticated_data, + )?; + + // construct a packet from the header and the cipher text + Ok(Packet { + iv, + header, + message: cipher, + }) + } + + /// Decrypts an encrypted message. If a Session is already established, the original decryption + /// keys are tried first, upon failure, the new keys are attempted. If the new keys succeed, + /// the session keys are updated along with the Session state. + pub(crate) fn decrypt_message( + &mut self, + message_nonce: MessageNonce, + message: &[u8], + aad: &[u8], + ) -> Result, Error> { + // First try with the canonical keys. + let result_canon = + crypto::decrypt_message(&self.keys.decryption_key, message_nonce, message, aad); + + // If decryption is fine, nothing more to do. + if result_canon.is_ok() { + return result_canon; + } + + // If these keys did not work, try old_keys + if let Some(old_keys) = self.old_keys.take() { + let result = + crypto::decrypt_message(&old_keys.decryption_key, message_nonce, message, aad); + if result.is_ok() { + // rotate the keys + self.old_keys = Some(std::mem::replace(&mut self.keys, old_keys)); + } + return result; + } + result_canon + } + + /* Session Helper Functions */ + + /// Generates session keys from an authentication header. If the IP of the ENR does not match the + /// source IP address, we consider this session untrusted. The output returns a boolean which + /// specifies if the Session is trusted or not. + pub(crate) fn establish_from_challenge( + local_key: Arc>, + local_id: &NodeId, + remote_id: &NodeId, + challenge: Challenge, + id_nonce_sig: &[u8], + ephem_pubkey: &[u8], + enr_record: Option, + ) -> Result<(Session, Enr), Error> { + // check and verify a potential ENR update + + // Duplicate code here to avoid cloning an ENR + let remote_public_key = { + let enr = match (enr_record.as_ref(), challenge.remote_enr.as_ref()) { + (Some(new_enr), Some(known_enr)) => { + if new_enr.seq() > known_enr.seq() { + new_enr + } else { + known_enr + } + } + (Some(new_enr), None) => new_enr, + (None, Some(known_enr)) => known_enr, + (None, None) => { + warn!( + "Peer did not respond with their ENR. Session could not be established. Node: {}", + remote_id + ); + return Err(Error::SessionNotEstablished); + } + }; + enr.public_key() + }; + + // verify the auth header nonce + if !crypto::verify_authentication_nonce( + &remote_public_key, + ephem_pubkey, + &challenge.data, + local_id, + id_nonce_sig, + ) { + return Err(Error::InvalidChallengeSignature(challenge)); + } + + // The keys are derived after the message has been verified to prevent potential extra work + // for invalid messages. + + // generate session keys + let (decryption_key, encryption_key) = crypto::derive_keys_from_pubkey( + &local_key.read(), + local_id, + remote_id, + &challenge.data, + ephem_pubkey, + )?; + + let keys = Keys { + encryption_key, + decryption_key, + }; + + // Takes ownership of the provided ENRs - Slightly annoying code duplication, but avoids + // cloning ENRs + let session_enr = match (enr_record, challenge.remote_enr) { + (Some(new_enr), Some(known_enr)) => { + if new_enr.seq() > known_enr.seq() { + new_enr + } else { + known_enr + } + } + (Some(new_enr), None) => new_enr, + (None, Some(known_enr)) => known_enr, + (None, None) => unreachable!("Checked in the first match above"), + }; + + Ok((Session::new(keys), session_enr)) + } + + /// Encrypts a message and produces an AuthMessage. + pub(crate) fn encrypt_with_header( + remote_contact: &NodeContact, + local_key: Arc>, + updated_enr: Option, + local_node_id: &NodeId, + challenge_data: &ChallengeData, + message: &[u8], + ) -> Result<(Packet, Session), Error> { + // generate the session keys + let (encryption_key, decryption_key, ephem_pubkey) = + crypto::generate_session_keys(local_node_id, remote_contact, challenge_data)?; + + let keys = Keys { + encryption_key, + decryption_key, + }; + + // construct the nonce signature + let sig = crypto::sign_nonce( + &local_key.read(), + challenge_data, + &ephem_pubkey, + &remote_contact.node_id(), + ) + .map_err(|_| Error::Custom("Could not sign WHOAREYOU nonce"))?; + + // build an authentication packet + let message_nonce: MessageNonce = rand::random(); + let mut packet = Packet::new_authheader( + *local_node_id, + message_nonce, + sig, + ephem_pubkey, + updated_enr, + ); + + // Create the authenticated data for the new packet. + + let mut authenticated_data = packet.iv.to_be_bytes().to_vec(); + authenticated_data.extend_from_slice(&packet.header.encode::

()); + + // encrypt the message + let message_ciphertext = + crypto::encrypt_message(&encryption_key, message_nonce, message, &authenticated_data)?; + + packet.message = message_ciphertext; + + let session = Session::new(keys); + + Ok((packet, session)) + } +} + +#[cfg(test)] +pub(crate) fn build_dummy_session() -> Session { + Session::new(Keys { + encryption_key: [0; 16], + decryption_key: [0; 16], + }) +} diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs index af2a74bfc..c6b4b4916 100644 --- a/src/handler/sessions/limiter.rs +++ b/src/handler/sessions/limiter.rs @@ -40,8 +40,9 @@ impl SessionLimiter { return Ok(()); } // Empty buffer of expired sessions, and remove any which belong to unreachable ENRs. - while let Ok(Some(session_index)) = self.rx_expired_sessions.try_next() { - self.sessions_unreachable_enr_tracker.remove(&session_index); + while let Ok(Some(session_node_address)) = self.rx_expired_sessions.try_next() { + self.sessions_unreachable_enr_tracker + .remove(&session_node_address); } // Peer is unreachable if self.sessions_unreachable_enr_tracker.len() >= self.limit { From a0d09c1e2e5aa6c5aac9c6e7cf7b7037a74a7e81 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 18:21:51 +0100 Subject: [PATCH 119/154] Update docs --- src/handler/sessions/limiter.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs index c6b4b4916..25e522fa3 100644 --- a/src/handler/sessions/limiter.rs +++ b/src/handler/sessions/limiter.rs @@ -30,7 +30,8 @@ impl SessionLimiter { } } - // Checks if a session with this peer should be allowed at this given time. + /// Checks if a session with this peer should be allowed at this given time. Called after + /// connection establishment. pub fn track_sessions_unreachable_enr( &mut self, node_address: &NodeAddress, From 89a81aefbfb6d2e3528bcb63ddceb85cc592bd4e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 18:22:44 +0100 Subject: [PATCH 120/154] Update comment Co-authored-by: Kolby Moroz Liebl <31669092+KolbyML@users.noreply.github.com> --- src/lru_time_cache.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lru_time_cache.rs b/src/lru_time_cache.rs index 482bb4f78..31200ae7a 100644 --- a/src/lru_time_cache.rs +++ b/src/lru_time_cache.rs @@ -127,7 +127,7 @@ impl LruTimeCache { if let Some(ref mut tx) = self.tx { for k in expired_keys { if let Err(e) = tx.try_send(k) { - warn!("remove expired failed, {}", e); + warn!("Failed removing expired key, {}", e); } } } From 35b4877144d9f2953eb19d5ecb8d38e1898a8459 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 18:41:16 +0100 Subject: [PATCH 121/154] fixup! Update docs --- src/handler/sessions/limiter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs index 25e522fa3..69404827d 100644 --- a/src/handler/sessions/limiter.rs +++ b/src/handler/sessions/limiter.rs @@ -30,7 +30,7 @@ impl SessionLimiter { } } - /// Checks if a session with this peer should be allowed at this given time. Called after + /// Checks if a session with this peer should be allowed at this given time. Called after /// connection establishment. pub fn track_sessions_unreachable_enr( &mut self, From a729371864f2fd44e7f3673da5ca0d4185e818a8 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Thu, 4 Jan 2024 18:43:20 +0100 Subject: [PATCH 122/154] Lint fixes --- src/handler/mod.rs | 2 +- src/handler/request_call.rs | 2 +- src/handler/sessions/mod.rs | 2 +- src/handler/sessions/session.rs | 10 +++++----- src/kbucket/entry.rs | 4 +--- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 34ad45727..82e4d11a2 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -539,7 +539,7 @@ impl Handler

{ trace!("Request queued for node: {}", node_address); self.pending_requests .entry(node_address) - .or_insert_with(Vec::new) + .or_default() .push(PendingRequest { contact, request_id, diff --git a/src/handler/request_call.rs b/src/handler/request_call.rs index a4f8993b9..a776dfdb1 100644 --- a/src/handler/request_call.rs +++ b/src/handler/request_call.rs @@ -1,4 +1,4 @@ -pub use crate::node_info::{NodeAddress, NodeContact}; +pub use crate::node_info::NodeContact; use crate::{ packet::Packet, rpc::{Payload, Request, RequestBody}, diff --git a/src/handler/sessions/mod.rs b/src/handler/sessions/mod.rs index 81ea5b608..0ff58ecbd 100644 --- a/src/handler/sessions/mod.rs +++ b/src/handler/sessions/mod.rs @@ -37,7 +37,7 @@ impl Sessions { Sessions { cache, limiter } } - pub fn insert(key: NodeAddress, value: Session) { + pub fn _insert(_key: NodeAddress, _value: Session) { todo!() } } diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index ffb234190..722031735 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -6,7 +6,7 @@ use crate::{ ChallengeData, MessageNonce, Packet, PacketHeader, PacketKind, ProtocolIdentity, MESSAGE_NONCE_LENGTH, }, - rpc::{Message, RequestId}, + rpc::RequestId, Discv5Error, Enr, }; @@ -228,7 +228,7 @@ impl Session { ) { let challenge = Challenge { data, - remote_enr: session_enr.to_challenge_enr(), + remote_enr: session_enr.into_challenge_enr(), }; return Err(Discv5Error::InvalidChallengeSignature(challenge)); } @@ -250,7 +250,7 @@ impl Session { decryption_key, }; - let session_enr = session_enr.to_most_recent_enr(); + let session_enr = session_enr.into_most_recent_enr(); Ok((Session::new(keys), session_enr)) } @@ -326,13 +326,13 @@ impl MostRecentEnr { } } - fn to_most_recent_enr(self) -> Enr { + fn into_most_recent_enr(self) -> Enr { match self { MostRecentEnr::Challenge { enr } => enr, MostRecentEnr::Handshake { enr, .. } => enr, } } - fn to_challenge_enr(self) -> Option { + fn into_challenge_enr(self) -> Option { match self { MostRecentEnr::Challenge { enr } => Some(enr), MostRecentEnr::Handshake { diff --git a/src/kbucket/entry.rs b/src/kbucket/entry.rs index 1e0304048..97be95de1 100644 --- a/src/kbucket/entry.rs +++ b/src/kbucket/entry.rs @@ -25,9 +25,7 @@ //! representing the nodes participating in the Kademlia DHT. pub use super::{ - bucket::{ - AppliedPending, ConnectionState, InsertResult, Node, NodeStatus, MAX_NODES_PER_BUCKET, - }, + bucket::{AppliedPending, ConnectionState, InsertResult, Node, NodeStatus}, key::*, ConnectionDirection, }; From b8169fc93862d5c7da63de626129f368a6a27c53 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 12:19:21 +0100 Subject: [PATCH 123/154] Lint fixes --- src/config.rs | 3 ++- src/executor.rs | 2 +- src/ipmode.rs | 24 ++++++++++++------------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/config.rs b/src/config.rs index f5b3d85b6..c385d71e3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,9 @@ +//! A set of configuration parameters to tune the discovery protocol. use crate::{ handler::MIN_SESSIONS_UNREACHABLE_ENR, ipmode::IpMode, kbucket::MAX_NODES_PER_BUCKET, Enr, Executor, PermitBanList, RateLimiter, RateLimiterBuilder, }; -///! A set of configuration parameters to tune the discovery protocol. + use std::{ops::RangeInclusive, time::Duration}; /// Configuration parameters that define the performance of the discovery network. diff --git a/src/executor.rs b/src/executor.rs index f3b6532e4..e3a1bee0a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,4 +1,4 @@ -///! A simple trait to allow generic executors or wrappers for spawning the discv5 tasks. +//! A simple trait to allow generic executors or wrappers for spawning the discv5 tasks. use std::future::Future; use std::pin::Pin; diff --git a/src/ipmode.rs b/src/ipmode.rs index cf527d8fa..f89119f4e 100644 --- a/src/ipmode.rs +++ b/src/ipmode.rs @@ -1,6 +1,6 @@ +//! A set of configuration parameters to tune the discovery protocol. use crate::Enr; use std::net::SocketAddr; -///! A set of configuration parameters to tune the discovery protocol. /// Sets the socket type to be established and also determines the type of ENRs that we will store /// in our routing table. @@ -54,6 +54,17 @@ impl IpMode { } } +/// Copied from the standard library. See https://github.com/rust-lang/rust/issues/27709 +/// The current code is behind the `ip` feature. +pub const fn to_ipv4_mapped(ip: &std::net::Ipv6Addr) -> Option { + match ip.octets() { + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { + Some(std::net::Ipv4Addr::new(a, b, c, d)) + } + _ => None, + } +} + #[cfg(test)] mod tests { use super::*; @@ -231,14 +242,3 @@ mod tests { .test(); } } - -/// Copied from the standard library. See https://github.com/rust-lang/rust/issues/27709 -/// The current code is behind the `ip` feature. -pub const fn to_ipv4_mapped(ip: &std::net::Ipv6Addr) -> Option { - match ip.octets() { - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { - Some(std::net::Ipv4Addr::new(a, b, c, d)) - } - _ => None, - } -} From 75627a5a0fa482023ded67cc2433e7341ddd8b7e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 12:24:04 +0100 Subject: [PATCH 124/154] fixup! Lint fixes --- src/executor.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index e3a1bee0a..04dac7e64 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,6 +1,5 @@ //! A simple trait to allow generic executors or wrappers for spawning the discv5 tasks. -use std::future::Future; -use std::pin::Pin; +use std::{future::Future, pin::Pin}; pub trait Executor: ExecutorClone { /// Run the given future in the background until it ends. From 421066bbb53a680a16b2a23c9dabba6b247d0ede Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 12:29:43 +0100 Subject: [PATCH 125/154] Fix docs --- src/handler/nat_hole_punch/utils.rs | 2 +- src/ipmode.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handler/nat_hole_punch/utils.rs b/src/handler/nat_hole_punch/utils.rs index 6581bba65..084115d12 100644 --- a/src/handler/nat_hole_punch/utils.rs +++ b/src/handler/nat_hole_punch/utils.rs @@ -21,7 +21,7 @@ pub const PORT_BIND_TRIES: usize = 4; /// Port range that is not impossible to bind to. pub const USER_AND_DYNAMIC_PORTS: RangeInclusive = 1025..=u16::MAX; -/// Aggregates types necessary to implement nat hole punching for [`Handler`]. +/// Aggregates types necessary to implement nat hole punching for [`crate::handler::Handler`]. pub struct NatHolePunchUtils { /// Ip mode as set in config. pub ip_mode: IpMode, diff --git a/src/ipmode.rs b/src/ipmode.rs index f89119f4e..2ba59fd11 100644 --- a/src/ipmode.rs +++ b/src/ipmode.rs @@ -54,7 +54,7 @@ impl IpMode { } } -/// Copied from the standard library. See https://github.com/rust-lang/rust/issues/27709 +/// Copied from the standard library. See /// The current code is behind the `ip` feature. pub const fn to_ipv4_mapped(ip: &std::net::Ipv6Addr) -> Option { match ip.octets() { From 1aed55d58cfc19789801177356cd207007be674f Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 16:47:25 +0100 Subject: [PATCH 126/154] Tweak incorporation of SessionLimiter --- src/handler/mod.rs | 12 +++++++++++- src/handler/sessions/limiter.rs | 27 ++++++++++++++++++++------- src/handler/sessions/mod.rs | 9 ++++----- src/handler/sessions/session.rs | 8 +++----- src/lru_time_cache.rs | 1 + 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 764b5b125..b02884f63 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -871,6 +871,8 @@ impl Handler { ); if let Some(challenge) = self.active_challenges.remove(&node_address) { + let session_limiter = self.sessions.limiter.as_mut(); + match Session::establish_from_challenge( self.key.clone(), &self.node_id, @@ -879,7 +881,10 @@ impl Handler { ephem_pubkey, enr_record, &node_address, - &mut self.sessions, + |node_address, enr| { + session_limiter + .map(|limiter| limiter.track_sessions_unreachable_enr(node_address, enr)) + }, ) { Ok((mut session, enr)) => { // Receiving an AuthResponse must give us an up-to-date view of the node ENR. @@ -1432,6 +1437,11 @@ impl Handler { .store(self.sessions.cache.len(), Ordering::Relaxed); // stop keeping hole punched for peer self.nat_hole_puncher.untrack(&node_address.socket_addr); + // update unreachable enr session limiter + self.sessions + .limiter + .as_mut() + .map(|limiter| limiter.untrack_session(node_address)); } if let Some(to_remove) = self.pending_requests.remove(node_address) { for PendingRequest { request_id, .. } in to_remove { diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs index 69404827d..c3416281c 100644 --- a/src/handler/sessions/limiter.rs +++ b/src/handler/sessions/limiter.rs @@ -30,27 +30,40 @@ impl SessionLimiter { } } + /// Drains buffer of expired sessions, and untracks any which belong to unreachable ENRs. + fn drain_expired_sessions_buffer(&mut self) { + while let Ok(Some(session_node_address)) = self.rx_expired_sessions.try_next() { + self.sessions_unreachable_enr_tracker + .remove(&session_node_address); + } + } + /// Checks if a session with this peer should be allowed at this given time. Called after - /// connection establishment. + /// connection establishment, before session key derivation. As a side effect this drains the + /// expired entries buffer. pub fn track_sessions_unreachable_enr( &mut self, node_address: &NodeAddress, enr: &Enr, ) -> Result<(), Discv5Error> { + self.drain_expired_sessions_buffer(); + + // Peer is reachable if enr.udp4_socket().is_some() || enr.udp6_socket().is_some() { return Ok(()); } - // Empty buffer of expired sessions, and remove any which belong to unreachable ENRs. - while let Ok(Some(session_node_address)) = self.rx_expired_sessions.try_next() { - self.sessions_unreachable_enr_tracker - .remove(&session_node_address); - } - // Peer is unreachable + // Peer is unreachable. if self.sessions_unreachable_enr_tracker.len() >= self.limit { return Err(Discv5Error::LimitSessionsUnreachableEnr); } + self.sessions_unreachable_enr_tracker .insert(node_address.clone()); Ok(()) } + + /// Untracks the given session if it has an unreachable ENR. + pub fn untrack_session(&mut self, node_address: &NodeAddress) { + self.sessions_unreachable_enr_tracker.remove(&node_address); + } } diff --git a/src/handler/sessions/mod.rs b/src/handler/sessions/mod.rs index 8f3c945d6..be4aa2c1c 100644 --- a/src/handler/sessions/mod.rs +++ b/src/handler/sessions/mod.rs @@ -10,8 +10,11 @@ pub use limiter::MIN_SESSIONS_UNREACHABLE_ENR; pub(crate) use session::Session; pub struct Sessions { + /// Stores established sessions. pub cache: LruTimeCache, - limiter: Option, + /// Limits the number of sessions to peers with unreachable ENRs. Limiter is queried upon + /// session establishment. + pub limiter: Option, } impl Sessions { @@ -36,10 +39,6 @@ impl Sessions { Sessions { cache, limiter } } - - pub fn _insert(_key: NodeAddress, _value: Session) { - todo!() - } } #[cfg(test)] diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index 397ab7f51..1210bcd97 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -178,7 +178,7 @@ impl Session { ephem_pubkey: &[u8], enr_record: Option, node_address: &NodeAddress, - sessions: &mut Sessions, + session_limiter: impl FnOnce(&NodeAddress, &Enr) -> Option>, ) -> Result<(Session, Enr), Discv5Error> { // check and verify a potential ENR update @@ -211,10 +211,8 @@ impl Session { }; // Avoid unnecessary key derivation computation, first verify session candidate against - // current sessions state - if let Some(ref mut limiter) = sessions.limiter { - limiter.track_sessions_unreachable_enr(node_address, session_enr.enr())?; - } + // current sessions state. + session_limiter(node_address, session_enr.enr()).transpose()?; let remote_public_key = session_enr.enr().public_key(); diff --git a/src/lru_time_cache.rs b/src/lru_time_cache.rs index 31200ae7a..79e8a9756 100644 --- a/src/lru_time_cache.rs +++ b/src/lru_time_cache.rs @@ -31,6 +31,7 @@ impl LruTimeCache { } } + /// Queues expired entries on buffer. If enabled, ensure that buffer is emptied regularly. pub fn new_with_expiry_feedback( ttl: Duration, capacity: Option, From a5ec452455572e8c1c5351162dcd42dfbf984e0c Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 17:04:22 +0100 Subject: [PATCH 127/154] Avoid clone --- src/handler/mod.rs | 57 +++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index b02884f63..32a4e168a 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -253,16 +253,33 @@ impl Handler { // The local node id let node_id = enr.read().node_id(); + let Discv5Config { + enable_packet_filter, + filter_rate_limiter, + filter_max_nodes_per_ip, + filter_max_bans_per_ip, + listen_config, + executor, + ban_duration, + session_cache_capacity, + session_timeout, + unreachable_enr_limit, + unused_port_range, + request_retries, + request_timeout, + .. + } = config; + // enable the packet filter if required let filter_config = FilterConfig { - enabled: config.enable_packet_filter, - rate_limiter: config.filter_rate_limiter.clone(), - max_nodes_per_ip: config.filter_max_nodes_per_ip, - max_bans_per_ip: config.filter_max_bans_per_ip, + enabled: enable_packet_filter, + rate_limiter: filter_rate_limiter, + max_nodes_per_ip: filter_max_nodes_per_ip, + max_bans_per_ip: filter_max_bans_per_ip, }; let mut listen_sockets = SmallVec::default(); - match config.listen_config { + match listen_config { ListenConfig::Ipv4 { ip, port } => listen_sockets.push((ip, port).into()), ListenConfig::Ipv6 { ip, port } => listen_sockets.push((ip, port).into()), ListenConfig::DualStack { @@ -276,46 +293,44 @@ impl Handler { } }; - let ip_mode = config.listen_config.ip_mode(); + let ip_mode = listen_config.ip_mode(); let socket_config = socket::SocketConfig { - executor: config.executor.clone().expect("Executor must exist"), + executor: executor.clone().expect("Executor must exist"), filter_config, - listen_config: config.listen_config.clone(), + listen_config, local_node_id: node_id, expected_responses: filter_expected_responses.clone(), - ban_duration: config.ban_duration, + ban_duration, }; // Attempt to bind to the socket before spinning up the send/recv tasks. let socket = Socket::new::

(socket_config).await?; let sessions = Sessions::new( - config.session_cache_capacity, - config.session_timeout, - config.unreachable_enr_limit, + session_cache_capacity, + session_timeout, + unreachable_enr_limit, ); let nat_hole_puncher = NatHolePunchUtils::new( listen_sockets.iter(), &enr.read(), ip_mode, - config.unused_port_range.clone(), - config.ban_duration, - config.session_cache_capacity, + unused_port_range, + ban_duration, + session_cache_capacity, ); - config - .executor - .clone() + executor .expect("Executor must be present") .spawn(Box::pin(async move { let mut handler = Handler { - request_retries: config.request_retries, + request_retries, node_id, enr, key, - active_requests: ActiveRequests::new(config.request_timeout), + active_requests: ActiveRequests::new(request_timeout), pending_requests: HashMap::new(), filter_expected_responses, sessions, @@ -323,7 +338,7 @@ impl Handler { Duration::from_secs(ONE_TIME_SESSION_TIMEOUT), Some(ONE_TIME_SESSION_CACHE_CAPACITY), ), - active_challenges: HashMapDelay::new(config.request_timeout), + active_challenges: HashMapDelay::new(request_timeout), service_recv, service_send, listen_sockets, From defa9ed902a807901502408fd097c97305171c3a Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 17:08:04 +0100 Subject: [PATCH 128/154] Lint fixes --- .github/workflows/build.yml | 2 +- src/handler/mod.rs | 7 +++---- src/handler/sessions/limiter.rs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4a744b916..15ea39692 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,7 +50,7 @@ jobs: - name: Get latest version of stable rust run: rustup update stable - name: Check rustdoc links - run: RUSTDOCFLAGS="--deny broken_intra_doc_links" cargo doc --verbose --workspace --no-deps --document-private-items + run: RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --verbose --workspace --no-deps --document-private-items cargo-udeps: name: cargo-udeps runs-on: ubuntu-latest diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 32a4e168a..301e72344 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1453,10 +1453,9 @@ impl Handler { // stop keeping hole punched for peer self.nat_hole_puncher.untrack(&node_address.socket_addr); // update unreachable enr session limiter - self.sessions - .limiter - .as_mut() - .map(|limiter| limiter.untrack_session(node_address)); + if let Some(ref mut limiter) = self.sessions.limiter { + limiter.untrack_session(node_address) + } } if let Some(to_remove) = self.pending_requests.remove(node_address) { for PendingRequest { request_id, .. } in to_remove { diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs index c3416281c..70db681b0 100644 --- a/src/handler/sessions/limiter.rs +++ b/src/handler/sessions/limiter.rs @@ -64,6 +64,6 @@ impl SessionLimiter { /// Untracks the given session if it has an unreachable ENR. pub fn untrack_session(&mut self, node_address: &NodeAddress) { - self.sessions_unreachable_enr_tracker.remove(&node_address); + self.sessions_unreachable_enr_tracker.remove(node_address); } } From 308d7934721f3d4b5c99befa8fa755a2a1aec11e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 17:20:51 +0100 Subject: [PATCH 129/154] Enable ipv6 tests --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 15ea39692..5fbe6ec54 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,7 @@ jobs: - name: Get latest version of stable rust run: rustup update stable - name: Run tests in release - run: cargo test --all --release --all-features -- --skip ipv6 # ipv6 tests don't run in github ci https://github.com/actions/runner-images/issues/668 + run: cargo test --all --release --all-features check-rustdoc-links: name: Check rustdoc intra-doc links runs-on: ubuntu-latest From d22647b45d1e66fdabe944aa0df6ac53f522deac Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 17:29:37 +0100 Subject: [PATCH 130/154] fixup! Fix merge conflicts with discv5.2 --- src/rpc/response.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rpc/response.rs b/src/rpc/response.rs index a852f1b08..e0978e6fa 100644 --- a/src/rpc/response.rs +++ b/src/rpc/response.rs @@ -101,8 +101,12 @@ impl Payload for Response { let mut ip = [0u8; 16]; ip.copy_from_slice(&ip_bytes); let ipv6 = Ipv6Addr::from(ip); - // If the ipv6 is ipv4 compatible/mapped, simply return the ipv4. - if let Some(ipv4) = ipv6.to_ipv4() { + if ipv6.is_loopback() { + // Checking if loopback address since IPv6Addr::to_ipv4 returns + // IPv4 address for IPv6 loopback address. + IpAddr::V6(ipv6) + } else if let Some(ipv4) = ipv6.to_ipv4() { + // If the ipv6 is ipv4 compatible/mapped, simply return the ipv4. IpAddr::V4(ipv4) } else { IpAddr::V6(ipv6) From 43be184dc2af2c1a319749b4b1a37e1ec3ce3bbc Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 17:52:51 +0100 Subject: [PATCH 131/154] fixup! Enable ipv6 tests --- src/handler/nat_hole_punch/utils.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/handler/nat_hole_punch/utils.rs b/src/handler/nat_hole_punch/utils.rs index 7718880e5..165e3add8 100644 --- a/src/handler/nat_hole_punch/utils.rs +++ b/src/handler/nat_hole_punch/utils.rs @@ -179,6 +179,8 @@ fn is_behind_nat(observed_ip: IpAddr, unused_port_range: &Option Date: Sat, 6 Jan 2024 17:57:11 +0100 Subject: [PATCH 132/154] Fix test parallelisation --- src/service/test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/service/test.rs b/src/service/test.rs index b2860700e..d02f8394c 100644 --- a/src/service/test.rs +++ b/src/service/test.rs @@ -173,7 +173,7 @@ async fn test_connection_direction_on_inject_session_established() { let ip = std::net::Ipv4Addr::LOCALHOST; let enr = EnrBuilder::new("v4") .ip4(ip) - .udp4(10001) + .udp4(10003) .build(&enr_key1) .unwrap(); @@ -181,7 +181,7 @@ async fn test_connection_direction_on_inject_session_established() { let ip2 = std::net::Ipv4Addr::LOCALHOST; let enr2 = EnrBuilder::new("v4") .ip4(ip2) - .udp4(10002) + .udp4(10004) .build(&enr_key2) .unwrap(); From 07b0c02a674c80275ded22ec9a8263ce361ead94 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 22:30:50 +0100 Subject: [PATCH 133/154] Add relay test for handler --- src/handler/sessions/session.rs | 3 +- src/handler/tests.rs | 212 +++++++++++++++++++++++++++----- 2 files changed, 186 insertions(+), 29 deletions(-) diff --git a/src/handler/sessions/session.rs b/src/handler/sessions/session.rs index 1210bcd97..209d551b2 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/sessions/session.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use tracing::warn; use zeroize::Zeroize; -#[derive(Zeroize, PartialEq)] +#[derive(Zeroize, PartialEq, Clone, Copy)] pub(crate) struct Keys { /// The encryption key. encryption_key: [u8; 16], @@ -39,6 +39,7 @@ impl From<([u8; 16], [u8; 16])> for Keys { /// A Session containing the encryption/decryption keys. These are kept individually for a given /// node. +#[derive(Clone)] pub(crate) struct Session { /// The current keys used to encrypt/decrypt messages. keys: Keys, diff --git a/src/handler/tests.rs b/src/handler/tests.rs index 0fe1f967e..701d2b4d7 100644 --- a/src/handler/tests.rs +++ b/src/handler/tests.rs @@ -3,7 +3,7 @@ use super::*; use crate::{ handler::sessions::session::build_dummy_session, - packet::DefaultProtocolId, + packet::{DefaultProtocolId, PacketHeader, MAX_PACKET_SIZE}, return_if_ipv6_is_not_supported, rpc::{Request, Response}, Discv5ConfigBuilder, IpMode, @@ -14,7 +14,7 @@ use crate::{handler::HandlerOut::RequestFailed, RequestError::SelfRequest}; use active_requests::ActiveRequests; use enr::EnrBuilder; use std::time::Duration; -use tokio::time::sleep; +use tokio::{net::UdpSocket, time::sleep}; fn init() { let _ = tracing_subscriber::fmt() @@ -22,7 +22,13 @@ fn init() { .try_init(); } -async fn build_handler() -> Handler { +struct MockService { + tx: mpsc::UnboundedSender, + rx: mpsc::Receiver, + exit_tx: oneshot::Sender<()>, +} + +async fn build_handler() -> (Handler, MockService) { let config = Discv5ConfigBuilder::new(ListenConfig::default()).build(); let key = CombinedKey::generate_secp256k1(); let enr = EnrBuilder::new("v4") @@ -56,9 +62,9 @@ async fn build_handler() -> Handler { Socket::new::

(socket_config).await.unwrap() }; - let (_, service_recv) = mpsc::unbounded_channel(); - let (service_send, _) = mpsc::channel(50); - let (_, exit) = oneshot::channel(); + let (handler_sender, service_recv) = mpsc::unbounded_channel(); + let (service_send, handler_recv) = mpsc::channel(50); + let (exit_tx, exit) = oneshot::channel(); let nat_hole_puncher = NatHolePunchUtils::new( listen_sockets.iter(), @@ -69,27 +75,34 @@ async fn build_handler() -> Handler { config.session_cache_capacity, ); - Handler { - request_retries: config.request_retries, - node_id, - enr: Arc::new(RwLock::new(enr)), - key: Arc::new(RwLock::new(key)), - active_requests: ActiveRequests::new(config.request_timeout), - pending_requests: HashMap::new(), - filter_expected_responses, - sessions: Sessions::new(config.session_cache_capacity, config.session_timeout, None), - one_time_sessions: LruTimeCache::new( - Duration::from_secs(ONE_TIME_SESSION_TIMEOUT), - Some(ONE_TIME_SESSION_CACHE_CAPACITY), - ), - active_challenges: HashMapDelay::new(config.request_timeout), - service_recv, - service_send, - listen_sockets, - socket, - nat_hole_puncher, - exit, - } + ( + Handler { + request_retries: config.request_retries, + node_id, + enr: Arc::new(RwLock::new(enr)), + key: Arc::new(RwLock::new(key)), + active_requests: ActiveRequests::new(config.request_timeout), + pending_requests: HashMap::new(), + filter_expected_responses, + sessions: Sessions::new(config.session_cache_capacity, config.session_timeout, None), + one_time_sessions: LruTimeCache::new( + Duration::from_secs(ONE_TIME_SESSION_TIMEOUT), + Some(ONE_TIME_SESSION_CACHE_CAPACITY), + ), + active_challenges: HashMapDelay::new(config.request_timeout), + service_recv, + service_send, + listen_sockets, + socket, + nat_hole_puncher, + exit, + }, + MockService { + tx: handler_sender, + rx: handler_recv, + exit_tx, + }, + ) } macro_rules! arc_rw { @@ -427,7 +440,7 @@ async fn test_self_request_ipv6() { #[tokio::test] async fn remove_one_time_session() { - let mut handler = build_handler::().await; + let (mut handler, _) = build_handler::().await; let enr = { let key = CombinedKey::generate_secp256k1(); @@ -461,3 +474,146 @@ async fn remove_one_time_session() { .is_some()); assert_eq!(0, handler.one_time_sessions.len()); } + +#[tokio::test(flavor = "multi_thread")] +async fn relay() { + init(); + + // Relay + let (mut handler, mock_service) = build_handler::().await; + let relay_addr = handler.enr.read().udp4_socket().unwrap().into(); + let relay_node_id = handler.enr.read().node_id(); + let mut dummy_session = build_dummy_session(); + + // Initiator + let initr_enr = { + let key = CombinedKey::generate_secp256k1(); + EnrBuilder::new("v4") + .ip4(Ipv4Addr::LOCALHOST) + .udp4(9011) + .build(&key) + .unwrap() + }; + let initr_addr = initr_enr.udp4_socket().unwrap().into(); + let initr_node_id = initr_enr.node_id(); + + let initr_node_address = NodeAddress::new(initr_addr, initr_enr.node_id()); + handler + .sessions + .cache + .insert(initr_node_address, dummy_session.clone()); + + let initr_socket = UdpSocket::bind(initr_addr) + .await + .expect("should bind to initiator socket"); + + // Target + let tgt_enr = { + let key = CombinedKey::generate_secp256k1(); + EnrBuilder::new("v4") + .ip4(Ipv4Addr::LOCALHOST) + .udp4(9012) + .build(&key) + .unwrap() + }; + let tgt_addr = tgt_enr.udp4_socket().unwrap().into(); + let tgt_node_id = tgt_enr.node_id(); + + let tgt_node_address = NodeAddress::new(tgt_addr, tgt_enr.node_id()); + handler + .sessions + .cache + .insert(tgt_node_address, dummy_session.clone()); + + let tgt_socket = UdpSocket::bind(tgt_addr) + .await + .expect("should bind to target socket"); + + // Relay handle + let relay_handle = tokio::spawn(async move { handler.start::().await }); + + // Relay mock service + let tgt_enr_clone = tgt_enr.clone(); + let tx = mock_service.tx; + let mut rx = mock_service.rx; + let mock_service_handle = tokio::spawn(async move { + let service_msg = rx.recv().await.expect("should receive service message"); + match service_msg { + HandlerOut::FindHolePunchEnr(_tgt_node_id, relay_msg_notif) => tx + .send(HandlerIn::HolePunchEnr(tgt_enr_clone, relay_msg_notif)) + .expect("should send message to handler"), + _ => panic!("service message should be 'find hole punch enr'"), + } + }); + + // Initiator handle + let relay_init_notif = + Notification::RelayInit(initr_enr.clone(), tgt_node_id, MessageNonce::default()); + + let initr_handle = tokio::spawn(async move { + let mut session = build_dummy_session(); + let packet = session + .encrypt_session_message::(initr_node_id, &relay_init_notif.encode()) + .expect("should encrypt notification"); + let encoded_packet = packet.encode::(&relay_node_id); + + initr_socket + .send_to(&encoded_packet, relay_addr) + .await + .expect("should relay init notification to relay") + }); + + // Target handle + let relay_exit = mock_service.exit_tx; + let tgt_handle = tokio::spawn(async move { + let mut buffer = [0; MAX_PACKET_SIZE]; + let res = tgt_socket + .recv_from(&mut buffer) + .await + .expect("should read bytes from socket"); + + drop(relay_exit); + + (res, buffer) + }); + + // Join all handles + let (initr_res, relay_res, tgt_res, mock_service_res) = + tokio::join!(initr_handle, relay_handle, tgt_handle, mock_service_handle); + + initr_res.unwrap(); + relay_res.unwrap(); + mock_service_res.unwrap(); + + let ((length, src), buffer) = tgt_res.unwrap(); + + assert_eq!(src, relay_addr); + + let (packet, aad) = Packet::decode::(&tgt_enr.node_id(), &buffer[..length]) + .expect("should decode packet"); + let Packet { + header, message, .. + } = packet; + let PacketHeader { + kind, + message_nonce, + .. + } = header; + + assert_eq!( + PacketKind::SessionMessage { + src_id: relay_node_id + }, + kind + ); + + let decrypted_message = dummy_session + .decrypt_message(message_nonce, &message, &aad) + .expect("should decrypt message"); + match Message::decode(&decrypted_message).expect("should decode message") { + Message::Notification(Notification::RelayMsg(enr, _nonce)) => { + assert_eq!(initr_enr, enr) + } + _ => panic!("message should decode to a relay msg notification"), + } +} From c5f281c289e13ab715117489ecee5ea8e7888499 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 22:33:17 +0100 Subject: [PATCH 134/154] Drive-by, fix param name --- src/packet/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packet/mod.rs b/src/packet/mod.rs index 9d5f03926..3207ea4b3 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -444,7 +444,7 @@ impl Packet { /// /// This also returns the authenticated data for further decryption in the handler. pub fn decode( - src_id: &NodeId, + dst_id: &NodeId, data: &[u8], ) -> Result<(Self, Vec), PacketError> { if data.len() > MAX_PACKET_SIZE { @@ -462,7 +462,7 @@ impl Packet { * This was split into its own library, but brought back to allow re-use of the cipher when * performing the decryption */ - let key = GenericArray::clone_from_slice(&src_id.raw()[..16]); + let key = GenericArray::clone_from_slice(&dst_id.raw()[..16]); let nonce = GenericArray::clone_from_slice(&iv); let mut cipher = Aes128Ctr::new(&key, &nonce); From c5d47bba74c76ad2156ef8fbdf0d6861cbb71cd0 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 22:33:33 +0100 Subject: [PATCH 135/154] Drive-by, clean up test --- src/rpc.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rpc.rs b/src/rpc.rs index 7aa312106..c26c690c2 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -83,6 +83,7 @@ impl Message { return Err(DecoderError::RlpIsTooShort); } let msg_type = data[0]; + let rlp = rlp::Rlp::new(&data[1..]); match msg_type.try_into()? { @@ -414,8 +415,6 @@ mod tests { let notif = Message::Notification(Notification::RelayInit(inr_enr, tgt_node_id, nonce)); - println!("{notif}"); - let encoded_notif = notif.clone().encode(); let decoded_notif = Message::decode(&encoded_notif).expect("Should decode"); From 91380188b6a14b05466800f8198186d013e1a62f Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sat, 6 Jan 2024 23:06:25 +0100 Subject: [PATCH 136/154] Fix handler test parallelisation --- src/handler/tests.rs | 19 +++++++++++++++---- src/socket/mod.rs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/handler/tests.rs b/src/handler/tests.rs index 701d2b4d7..15b1946ed 100644 --- a/src/handler/tests.rs +++ b/src/handler/tests.rs @@ -29,15 +29,24 @@ struct MockService { } async fn build_handler() -> (Handler, MockService) { - let config = Discv5ConfigBuilder::new(ListenConfig::default()).build(); + build_handler_with_listen_config::

(ListenConfig::default()).await +} + +async fn build_handler_with_listen_config( + listen_config: ListenConfig, +) -> (Handler, MockService) { + let listen_port = listen_config + .ipv4_port() + .expect("listen config should default to ipv4"); + let config = Discv5ConfigBuilder::new(listen_config).build(); let key = CombinedKey::generate_secp256k1(); let enr = EnrBuilder::new("v4") .ip4(Ipv4Addr::LOCALHOST) - .udp4(9000) + .udp4(listen_port) .build(&key) .unwrap(); let mut listen_sockets = SmallVec::default(); - listen_sockets.push((Ipv4Addr::LOCALHOST, 9000).into()); + listen_sockets.push((Ipv4Addr::LOCALHOST, listen_port).into()); let node_id = enr.node_id(); let filter_expected_responses = Arc::new(RwLock::new(HashMap::new())); @@ -480,7 +489,9 @@ async fn relay() { init(); // Relay - let (mut handler, mock_service) = build_handler::().await; + let listen_config = ListenConfig::default().with_ipv4(Ipv4Addr::LOCALHOST, 9901); + let (mut handler, mock_service) = + build_handler_with_listen_config::(listen_config).await; let relay_addr = handler.enr.read().udp4_socket().unwrap().into(); let relay_node_id = handler.enr.read().node_id(); let mut dummy_session = build_dummy_session(); diff --git a/src/socket/mod.rs b/src/socket/mod.rs index e0d64dfaf..66c07d857 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -47,6 +47,42 @@ pub enum ListenConfig { }, } +impl ListenConfig { + pub fn ipv4(&self) -> Option { + match self { + ListenConfig::Ipv4 { ip, .. } | ListenConfig::DualStack { ipv4: ip, .. } => Some(*ip), + _ => None, + } + } + + pub fn ipv6(&self) -> Option { + match self { + ListenConfig::Ipv6 { ip, .. } | ListenConfig::DualStack { ipv6: ip, .. } => Some(*ip), + _ => None, + } + } + + pub fn ipv4_port(&self) -> Option { + match self { + ListenConfig::Ipv4 { port, .. } + | ListenConfig::DualStack { + ipv4_port: port, .. + } => Some(*port), + _ => None, + } + } + + pub fn ipv6_port(&self) -> Option { + match self { + ListenConfig::Ipv6 { port, .. } + | ListenConfig::DualStack { + ipv6_port: port, .. + } => Some(*port), + _ => None, + } + } +} + /// Convenience objects for setting up the recv handler. pub struct SocketConfig { /// The executor to spawn the tasks. From c94c7e1a9892b2ba4ed5166ef9a31b159865c863 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 14 Jan 2024 05:52:42 +0100 Subject: [PATCH 137/154] Skip unnecessary interaction with service task --- src/handler/mod.rs | 24 +++++++----------------- src/handler/nat_hole_punch/mod.rs | 2 +- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 301e72344..6c0a859a1 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1112,7 +1112,7 @@ impl Handler { debug!("peer {node_address} relayed a hole punch notification but we are not behind nat"); } _ => { - if let Err(e) = self.on_relay_msg(initr, timed_out_nonce).await { + if let Err(e) = self.on_relay_msg::

(initr, timed_out_nonce).await { warn!( "failed handling notification relayed from {node_address}, {e}" ); @@ -1590,7 +1590,7 @@ impl HolePunchNat for Handler { Ok(()) } - async fn on_relay_msg( + async fn on_relay_msg( &mut self, initr: Enr, timed_out_nonce: MessageNonce, @@ -1603,10 +1603,7 @@ impl HolePunchNat for Handler { // A session may already have been established. if self.sessions.cache.get(&initiator_node_address).is_some() { - trace!( - "Session already established with initiator: {}", - initiator_node_address - ); + trace!("Session already established with initiator: {initiator_node_address}"); return Ok(()); } // Possibly, an attempt to punch this hole, using another relay, is in progress. @@ -1615,22 +1612,15 @@ impl HolePunchNat for Handler { .get(&initiator_node_address) .is_some() { - trace!( - "WHOAREYOU packet already sent to initiator: {}", - initiator_node_address - ); + trace!("WHOAREYOU packet already sent to initiator: {initiator_node_address}"); return Ok(()); } + // If not hole punch attempts are in progress, spawn a WHOAREYOU event to punch a hole in // our NAT for initiator. let whoareyou_ref = WhoAreYouRef(initiator_node_address, timed_out_nonce); - if let Err(e) = self - .service_send - .send(HandlerOut::WhoAreYou(whoareyou_ref)) - .await - { - return Err(NatHolePunchError::Target(e.into())); - } + self.send_challenge::

(whoareyou_ref, None).await; + Ok(()) } diff --git a/src/handler/nat_hole_punch/mod.rs b/src/handler/nat_hole_punch/mod.rs index e59166bce..f2cd009f4 100644 --- a/src/handler/nat_hole_punch/mod.rs +++ b/src/handler/nat_hole_punch/mod.rs @@ -36,7 +36,7 @@ pub trait HolePunchNat { /// A RelayMsg notification is received over discv5 indicating this node is the target. Should /// trigger a WHOAREYOU to be sent to the initiator using the `nonce` in the RelayMsg. - async fn on_relay_msg( + async fn on_relay_msg( &mut self, initr: Enr, timed_out_nonce: MessageNonce, From 3620b37f79414bd94532f4d43f70fd2232d2790e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 14 Jan 2024 05:53:25 +0100 Subject: [PATCH 138/154] Add test for target --- src/handler/tests.rs | 112 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/src/handler/tests.rs b/src/handler/tests.rs index 15b1946ed..850afe837 100644 --- a/src/handler/tests.rs +++ b/src/handler/tests.rs @@ -3,7 +3,7 @@ use super::*; use crate::{ handler::sessions::session::build_dummy_session, - packet::{DefaultProtocolId, PacketHeader, MAX_PACKET_SIZE}, + packet::{DefaultProtocolId, PacketHeader, MAX_PACKET_SIZE, MESSAGE_NONCE_LENGTH}, return_if_ipv6_is_not_supported, rpc::{Request, Response}, Discv5ConfigBuilder, IpMode, @@ -485,7 +485,7 @@ async fn remove_one_time_session() { } #[tokio::test(flavor = "multi_thread")] -async fn relay() { +async fn nat_hole_punch_relay() { init(); // Relay @@ -628,3 +628,111 @@ async fn relay() { _ => panic!("message should decode to a relay msg notification"), } } + +#[tokio::test(flavor = "multi_thread")] +async fn nat_hole_punch_target() { + init(); + + // Target + let listen_config = ListenConfig::default().with_ipv4(Ipv4Addr::LOCALHOST, 9902); + let (mut handler, mock_service) = + build_handler_with_listen_config::(listen_config).await; + let tgt_addr = handler.enr.read().udp4_socket().unwrap().into(); + let tgt_node_id = handler.enr.read().node_id(); + let dummy_session = build_dummy_session(); + handler.nat_hole_puncher.is_behind_nat = Some(true); + + // Relay + let relay_enr = { + let key = CombinedKey::generate_secp256k1(); + EnrBuilder::new("v4") + .ip4(Ipv4Addr::LOCALHOST) + .udp4(9022) + .build(&key) + .unwrap() + }; + let relay_addr = relay_enr.udp4_socket().unwrap().into(); + let relay_node_id = relay_enr.node_id(); + + let relay_node_address = NodeAddress::new(relay_addr, relay_node_id); + handler + .sessions + .cache + .insert(relay_node_address, dummy_session.clone()); + + let relay_socket = UdpSocket::bind(relay_addr) + .await + .expect("should bind to target socket"); + + // Initiator + let initr_enr = { + let key = CombinedKey::generate_secp256k1(); + EnrBuilder::new("v4") + .ip4(Ipv4Addr::LOCALHOST) + .udp4(9021) + .build(&key) + .unwrap() + }; + let initr_addr = initr_enr.udp4_socket().unwrap(); + let initr_node_id = initr_enr.node_id(); + let initr_nonce: MessageNonce = [1; MESSAGE_NONCE_LENGTH]; + + let initr_socket = UdpSocket::bind(initr_addr) + .await + .expect("should bind to initiator socket"); + + // Target handle + let tgt_handle = tokio::spawn(async move { handler.start::().await }); + + // Relay handle + let relay_msg_notif = Notification::RelayMsg(initr_enr.clone(), initr_nonce); + + let relay_handle = tokio::spawn(async move { + let mut session = build_dummy_session(); + let packet = session + .encrypt_session_message::(relay_node_id, &relay_msg_notif.encode()) + .expect("should encrypt notification"); + let encoded_packet = packet.encode::(&tgt_node_id); + + relay_socket + .send_to(&encoded_packet, tgt_addr) + .await + .expect("should relay init notification to relay") + }); + + // Initiator handle + let target_exit = mock_service.exit_tx; + let initr_handle = tokio::spawn(async move { + let mut buffer = [0; MAX_PACKET_SIZE]; + let res = initr_socket + .recv_from(&mut buffer) + .await + .expect("should read bytes from socket"); + + drop(target_exit); + + (res, buffer) + }); + + // Join all handles + let (tgt_res, relay_res, initr_res) = tokio::join!(tgt_handle, relay_handle, initr_handle); + + tgt_res.unwrap(); + relay_res.unwrap(); + + let ((length, src), buffer) = initr_res.unwrap(); + + assert_eq!(src, tgt_addr); + + let (packet, _aad) = Packet::decode::(&initr_node_id, &buffer[..length]) + .expect("should decode packet"); + let Packet { header, .. } = packet; + let PacketHeader { + kind, + message_nonce, + .. + } = header; + + assert!(kind.is_whoareyou()); + assert_eq!(message_nonce, initr_nonce) +} From b32e750736769d8f84143b1cb23b54c84f5d8d24 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 14 Jan 2024 07:12:06 +0100 Subject: [PATCH 139/154] Remove unnecessary memory re-allocation for sessions cache --- src/handler/mod.rs | 22 +++++++++------------- src/service.rs | 16 ++++++---------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 6c0a859a1..d5ac1131c 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -117,8 +117,8 @@ pub enum HandlerIn { /// [`Notification::RelayMsg`] we intend to relay to that peer. HolePunchEnr(Enr, Notification), - /// Observed socket has been update. The old socket and the current socket. - SocketUpdate(Option, SocketAddr), + /// Observed socket has been update, contains the current socket. + SocketUpdate(SocketAddr), } /// Messages sent between a node on the network and `Handler`. @@ -377,19 +377,15 @@ impl Handler { warn!("Failed to relay. Error: {}", e); } } - HandlerIn::SocketUpdate(old_socket, socket) => { + HandlerIn::SocketUpdate(socket) => { let ip = socket.ip(); let port = socket.port(); - if old_socket.is_none() { - // This node goes from being unreachable to being reachable. Remove - // its sessions to trigger a WHOAREYOU from peers on next sent - // message. If the peer is running this implementation of - // discovery, this makes it possible for the local node to be - // inserted into its peers' kbuckets before the session they - // already had expires. Session duration, in this impl defaults to - // 24 hours. - self.sessions.cache.clear() - } + // This node goes from being unreachable to being reachable. + // Reasonably assuming all its peers are indexing sessions based on + // `node_id`, like this implementation, the first message sent in each + // session from here on will trigger a WHOAREYOU message from the peer + // (since the peer won't be able to find the decryption key for the + // session with the new node id as message's src id). self.nat_hole_puncher.set_is_behind_nat(self.listen_sockets.iter(), Some(ip), Some(port)); } } diff --git a/src/service.rs b/src/service.rs index 17258be7e..ea72e5339 100644 --- a/src/service.rs +++ b/src/service.rs @@ -855,11 +855,9 @@ impl Service { new_ip6, )); // Notify Handler of socket update - if let Err(e) = - self.handler_send.send(HandlerIn::SocketUpdate( - local_ip6_socket.map(SocketAddr::V6), - new_ip6, - )) + if let Err(e) = self + .handler_send + .send(HandlerIn::SocketUpdate(new_ip6)) { warn!("Failed to send socket update to handler: {}", e); }; @@ -883,11 +881,9 @@ impl Service { new_ip4, )); // Notify Handler of socket update - if let Err(e) = - self.handler_send.send(HandlerIn::SocketUpdate( - local_ip4_socket.map(SocketAddr::V4), - new_ip4, - )) + if let Err(e) = self + .handler_send + .send(HandlerIn::SocketUpdate(new_ip4)) { warn!("Failed to send socket update {}", e); }; From b94736464ad2dc639895b86d49a4658fcdeac56a Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 14 Jan 2024 07:40:48 +0100 Subject: [PATCH 140/154] Reset previous commit --- src/handler/mod.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index d5ac1131c..2915377be 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -117,8 +117,8 @@ pub enum HandlerIn { /// [`Notification::RelayMsg`] we intend to relay to that peer. HolePunchEnr(Enr, Notification), - /// Observed socket has been update, contains the current socket. - SocketUpdate(SocketAddr), + /// Observed socket has been update. The old socket and the current socket. + SocketUpdate(Option, SocketAddr), } /// Messages sent between a node on the network and `Handler`. @@ -377,15 +377,20 @@ impl Handler { warn!("Failed to relay. Error: {}", e); } } - HandlerIn::SocketUpdate(socket) => { + HandlerIn::SocketUpdate(old_socket, socket) => { let ip = socket.ip(); let port = socket.port(); - // This node goes from being unreachable to being reachable. - // Reasonably assuming all its peers are indexing sessions based on - // `node_id`, like this implementation, the first message sent in each - // session from here on will trigger a WHOAREYOU message from the peer - // (since the peer won't be able to find the decryption key for the - // session with the new node id as message's src id). + if old_socket.is_none() { + // This node goes from being unreachable to being reachable, but + // keeps the same enr key (hence same node id). Remove its + // sessions to trigger a WHOAREYOU from peers on next sent + // message. If the peer is running this implementation of + // discovery, this makes it possible for the local node to be + // inserted into its peers' kbuckets before the session they + // already had expires. Session duration, in this impl defaults to + // 24 hours. + self.sessions.cache.clear() + } self.nat_hole_puncher.set_is_behind_nat(self.listen_sockets.iter(), Some(ip), Some(port)); } } From e0a2d4af5eecd142c79f7f80e5a3ff32dbae88d0 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 14 Jan 2024 09:09:58 +0100 Subject: [PATCH 141/154] Improve type safety for notifications --- src/handler/mod.rs | 110 ++++++++--------- src/handler/nat_hole_punch/mod.rs | 19 ++- src/handler/tests.rs | 11 +- src/kbucket/entry.rs | 4 +- src/rpc.rs | 42 ++++--- src/rpc/notification.rs | 197 +++++++++++++++++++----------- src/service.rs | 53 +++++--- 7 files changed, 248 insertions(+), 188 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 2915377be..c1c05fba9 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -32,7 +32,8 @@ use crate::{ error::{Discv5Error, RequestError}, packet::{ChallengeData, IdNonce, MessageNonce, Packet, PacketKind, ProtocolIdentity}, rpc::{ - Message, Notification, Payload, Request, RequestBody, RequestId, Response, ResponseBody, + Message, Payload, RelayInitNotification, RelayMsgNotification, Request, RequestBody, + RequestId, Response, ResponseBody, }, socket, socket::{FilterConfig, Outbound, Socket}, @@ -114,8 +115,8 @@ pub enum HandlerIn { WhoAreYou(WhoAreYouRef, Option), /// A response to a [`HandlerOut::FindHolePunchEnr`]. Returns the ENR and the - /// [`Notification::RelayMsg`] we intend to relay to that peer. - HolePunchEnr(Enr, Notification), + /// [`RelayInitNotification`] from [`HandlerOut::FindHolePunchEnr`]. + HolePunchEnr(Enr, RelayInitNotification), /// Observed socket has been update. The old socket and the current socket. SocketUpdate(Option, SocketAddr), @@ -147,8 +148,8 @@ pub enum HandlerOut { RequestFailed(RequestId, RequestError), /// Look-up an ENR in k-buckets. Passes the node id of the peer to look up and the - /// [`Notification::RelayMsg`] we intend to send to it. - FindHolePunchEnr(NodeId, Notification), + /// [`RelayMsgNotification`] we intend to send to it. + FindHolePunchEnr(RelayInitNotification), } /// How we connected to the node. @@ -372,7 +373,10 @@ impl Handler { } HandlerIn::Response(dst, response) => self.send_response::

(dst, *response).await, HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge::

(wru_ref, enr).await, - HandlerIn::HolePunchEnr(tgt_enr, relay_msg_notif) => { + HandlerIn::HolePunchEnr(tgt_enr, relay_init) => { + // Assemble the notification for the target + let (initr_enr, _tgt, timed_out_nonce) = relay_init.into(); + let relay_msg_notif = RelayMsgNotification::new(initr_enr, timed_out_nonce); if let Err(e) = self.send_relay_msg_notif::

(tgt_enr, relay_msg_notif).await { warn!("Failed to relay. Error: {}", e); } @@ -380,17 +384,12 @@ impl Handler { HandlerIn::SocketUpdate(old_socket, socket) => { let ip = socket.ip(); let port = socket.port(); - if old_socket.is_none() { - // This node goes from being unreachable to being reachable, but - // keeps the same enr key (hence same node id). Remove its - // sessions to trigger a WHOAREYOU from peers on next sent - // message. If the peer is running this implementation of - // discovery, this makes it possible for the local node to be - // inserted into its peers' kbuckets before the session they - // already had expires. Session duration, in this impl defaults to - // 24 hours. - self.sessions.cache.clear() - } + // This node goes from being unreachable to being reachable. + // Reasonably assuming all its peers are indexing sessions based on + // `node_id`, like this implementation, the first message sent in each + // session from here on will trigger a WHOAREYOU message from the peer + // (since the peer won't be able to find the decryption key for the + // session with the new node id as message's src id). self.nat_hole_puncher.set_is_behind_nat(self.listen_sockets.iter(), Some(ip), Some(port)); } } @@ -1089,39 +1088,35 @@ impl Handler { match message { Message::Response(response) => self.handle_response::

(node_address, response).await, - Message::Notification(notif) => match notif { - Notification::RelayInit(initr, tgt, timed_out_nonce) => { - let initr_node_id = initr.node_id(); - if initr_node_id != node_address.node_id { - warn!("peer {node_address} tried to initiate hole punch attempt for another node {initr_node_id}, banning peer {node_address}"); - self.fail_session(&node_address, RequestError::MaliciousRelayInit, true) - .await; - let ban_timeout = self - .nat_hole_puncher - .ban_duration - .map(|v| Instant::now() + v); - PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); - } else if let Err(e) = self.on_relay_init(initr, tgt, timed_out_nonce).await { - warn!("failed handling notification to relay for {node_address}, {e}"); - } + Message::RelayInitNotification(notif) => { + let initr_node_id = notif.initiator_enr().node_id(); + if initr_node_id != node_address.node_id { + warn!("peer {node_address} tried to initiate hole punch attempt for another node {initr_node_id}, banning peer {node_address}"); + self.fail_session(&node_address, RequestError::MaliciousRelayInit, true) + .await; + let ban_timeout = self + .nat_hole_puncher + .ban_duration + .map(|v| Instant::now() + v); + PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); + } else if let Err(e) = self.on_relay_init(notif).await { + warn!("failed handling notification to relay for {node_address}, {e}"); } - Notification::RelayMsg(initr, timed_out_nonce) => { - match self.nat_hole_puncher.is_behind_nat { - Some(false) => { - // initr may not be malicious and initiated a hole punch attempt when - // a request to this node timed out for another reason - debug!("peer {node_address} relayed a hole punch notification but we are not behind nat"); - } - _ => { - if let Err(e) = self.on_relay_msg::

(initr, timed_out_nonce).await { - warn!( - "failed handling notification relayed from {node_address}, {e}" - ); - } + } + Message::RelayMsgNotification(notif) => { + match self.nat_hole_puncher.is_behind_nat { + Some(false) => { + // initr may not be malicious and initiated a hole punch attempt when + // a request to this node timed out for another reason + debug!("peer {node_address} relayed a hole punch notification but we are not behind nat"); + } + _ => { + if let Err(e) = self.on_relay_msg::

(notif).await { + warn!("failed handling notification relayed from {node_address}, {e}"); } } } - }, + } Message::Request(_) => { warn!( "Peer sent message type {} that shouldn't be sent in packet type `Session Message`, {}", @@ -1200,7 +1195,7 @@ impl Handler { warn!("Received a response in a `Message` packet, should be sent in a `SessionMessage`"); self.handle_response::

(node_address, response).await } - Message::Notification(_) => { + Message::RelayInitNotification(_) | Message::RelayMsgNotification(_) => { warn!( "Peer sent message type {} that shouldn't be sent in packet type `Message`, {}", message.msg_type(), @@ -1542,7 +1537,7 @@ impl HolePunchNat for Handler { } if let Some(session) = self.sessions.cache.get_mut(&relay) { let relay_init_notif = - Notification::RelayInit(local_enr, target_node_address.node_id, timed_out_nonce); + RelayInitNotification::new(local_enr, target_node_address.node_id, timed_out_nonce); trace!( "Sending notif to relay {}. relay init: {}", relay.node_id, @@ -1573,17 +1568,12 @@ impl HolePunchNat for Handler { async fn on_relay_init( &mut self, - initr: Enr, - tgt: NodeId, - timed_out_nonce: MessageNonce, + relay_init: RelayInitNotification, ) -> Result<(), NatHolePunchError> { - // Assemble the notification for the target - let relay_msg_notif = Notification::RelayMsg(initr, timed_out_nonce); - // Check for target peer in our kbuckets otherwise drop notification. if let Err(e) = self .service_send - .send(HandlerOut::FindHolePunchEnr(tgt, relay_msg_notif)) + .send(HandlerOut::FindHolePunchEnr(relay_init)) .await { return Err(NatHolePunchError::Relay(e.into())); @@ -1593,11 +1583,11 @@ impl HolePunchNat for Handler { async fn on_relay_msg( &mut self, - initr: Enr, - timed_out_nonce: MessageNonce, + relay_msg: RelayMsgNotification, ) -> Result<(), NatHolePunchError> { + let (initr_enr, timed_out_msg_nonce) = relay_msg.into(); let initiator_node_address = - match NodeContact::try_from_enr(initr, self.nat_hole_puncher.ip_mode) { + match NodeContact::try_from_enr(initr_enr, self.nat_hole_puncher.ip_mode) { Ok(contact) => contact.node_address(), Err(e) => return Err(NatHolePunchError::Target(e.into())), }; @@ -1619,7 +1609,7 @@ impl HolePunchNat for Handler { // If not hole punch attempts are in progress, spawn a WHOAREYOU event to punch a hole in // our NAT for initiator. - let whoareyou_ref = WhoAreYouRef(initiator_node_address, timed_out_nonce); + let whoareyou_ref = WhoAreYouRef(initiator_node_address, timed_out_msg_nonce); self.send_challenge::

(whoareyou_ref, None).await; Ok(()) @@ -1628,7 +1618,7 @@ impl HolePunchNat for Handler { async fn send_relay_msg_notif( &mut self, tgt_enr: Enr, - relay_msg_notif: Notification, + relay_msg_notif: RelayMsgNotification, ) -> Result<(), NatHolePunchError> { let tgt_node_address = match NodeContact::try_from_enr(tgt_enr, self.nat_hole_puncher.ip_mode) { diff --git a/src/handler/nat_hole_punch/mod.rs b/src/handler/nat_hole_punch/mod.rs index f2cd009f4..1d1d26a14 100644 --- a/src/handler/nat_hole_punch/mod.rs +++ b/src/handler/nat_hole_punch/mod.rs @@ -1,9 +1,10 @@ use std::net::SocketAddr; -use enr::NodeId; - use crate::{ - node_info::NodeAddress, packet::MessageNonce, rpc::Notification, Enr, ProtocolIdentity, + node_info::NodeAddress, + packet::MessageNonce, + rpc::{RelayInitNotification, RelayMsgNotification}, + Enr, ProtocolIdentity, }; mod error; @@ -27,26 +28,20 @@ pub trait HolePunchNat { /// A RelayInit notification is received over discv5 indicating this node is the relay. Should /// trigger sending a RelayMsg to the target. - async fn on_relay_init( - &mut self, - initr: Enr, - tgt: NodeId, - timed_out_nonce: MessageNonce, - ) -> Result<(), Error>; + async fn on_relay_init(&mut self, relay_init: RelayInitNotification) -> Result<(), Error>; /// A RelayMsg notification is received over discv5 indicating this node is the target. Should /// trigger a WHOAREYOU to be sent to the initiator using the `nonce` in the RelayMsg. async fn on_relay_msg( &mut self, - initr: Enr, - timed_out_nonce: MessageNonce, + relay_msg: RelayMsgNotification, ) -> Result<(), Error>; /// Send a RELAYMSG notification. async fn send_relay_msg_notif( &mut self, tgt_enr: Enr, - relay_msg_notif: Notification, + relay_msg_notif: RelayMsgNotification, ) -> Result<(), Error>; /// A hole punched for a peer closes. Should trigger an empty packet to be sent to the diff --git a/src/handler/tests.rs b/src/handler/tests.rs index 850afe837..67d3b5f9c 100644 --- a/src/handler/tests.rs +++ b/src/handler/tests.rs @@ -550,8 +550,8 @@ async fn nat_hole_punch_relay() { let mock_service_handle = tokio::spawn(async move { let service_msg = rx.recv().await.expect("should receive service message"); match service_msg { - HandlerOut::FindHolePunchEnr(_tgt_node_id, relay_msg_notif) => tx - .send(HandlerIn::HolePunchEnr(tgt_enr_clone, relay_msg_notif)) + HandlerOut::FindHolePunchEnr(relay_init) => tx + .send(HandlerIn::HolePunchEnr(tgt_enr_clone, relay_init)) .expect("should send message to handler"), _ => panic!("service message should be 'find hole punch enr'"), } @@ -559,7 +559,7 @@ async fn nat_hole_punch_relay() { // Initiator handle let relay_init_notif = - Notification::RelayInit(initr_enr.clone(), tgt_node_id, MessageNonce::default()); + RelayInitNotification::new(initr_enr.clone(), tgt_node_id, MessageNonce::default()); let initr_handle = tokio::spawn(async move { let mut session = build_dummy_session(); @@ -622,7 +622,8 @@ async fn nat_hole_punch_relay() { .decrypt_message(message_nonce, &message, &aad) .expect("should decrypt message"); match Message::decode(&decrypted_message).expect("should decode message") { - Message::Notification(Notification::RelayMsg(enr, _nonce)) => { + Message::RelayMsgNotification(relay_msg) => { + let (enr, _) = relay_msg.into(); assert_eq!(initr_enr, enr) } _ => panic!("message should decode to a relay msg notification"), @@ -685,7 +686,7 @@ async fn nat_hole_punch_target() { let tgt_handle = tokio::spawn(async move { handler.start::().await }); // Relay handle - let relay_msg_notif = Notification::RelayMsg(initr_enr.clone(), initr_nonce); + let relay_msg_notif = RelayMsgNotification::new(initr_enr.clone(), initr_nonce); let relay_handle = tokio::spawn(async move { let mut session = build_dummy_session(); diff --git a/src/kbucket/entry.rs b/src/kbucket/entry.rs index 97be95de1..101d637c2 100644 --- a/src/kbucket/entry.rs +++ b/src/kbucket/entry.rs @@ -174,8 +174,8 @@ where PendingEntry(EntryRef { bucket, key }) } - /// Returns the value associated with the key. - pub fn value(&mut self) -> &mut TVal { + /// Returns mutable access value associated with the key. + pub fn value_mut(&mut self) -> &mut TVal { self.0 .bucket .pending_mut() diff --git a/src/rpc.rs b/src/rpc.rs index c26c690c2..974cb8d4b 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -6,7 +6,7 @@ mod notification; mod request; mod response; -pub use notification::Notification; +pub use notification::{RelayInitNotification, RelayMsgNotification}; pub use request::{Request, RequestBody, RequestId}; pub use response::{Response, ResponseBody}; @@ -60,12 +60,19 @@ pub enum Message { /// A request, which contains its [`RequestId`]. #[display(fmt = "{_0}")] Request(Request), + /// A Response, which contains the [`RequestId`] of its associated request. #[display(fmt = "{_0}")] Response(Response), - /// A unicast notification. + + /// Unicast notifications. + /// + /// A [`RelayInitNotification`]. #[display(fmt = "{_0}")] - Notification(Notification), + RelayInitNotification(RelayInitNotification), + /// A [`RelayMsgNotification`]. + #[display(fmt = "{_0}")] + RelayMsgNotification(RelayMsgNotification), } #[allow(dead_code)] @@ -74,7 +81,8 @@ impl Message { match self { Self::Request(request) => request.encode(), Self::Response(response) => response.encode(), - Self::Notification(notif) => notif.encode(), + Self::RelayInitNotification(notif) => notif.encode(), + Self::RelayMsgNotification(notif) => notif.encode(), } } @@ -93,17 +101,17 @@ impl Message { MessageType::Pong | MessageType::Nodes | MessageType::TalkResp => { Ok(Response::decode(msg_type, &rlp)?.into()) } - MessageType::RelayInit | MessageType::RelayMsg => { - Ok(Notification::decode(msg_type, &rlp)?.into()) - } + MessageType::RelayInit => Ok(RelayInitNotification::decode(msg_type, &rlp)?.into()), + MessageType::RelayMsg => Ok(RelayMsgNotification::decode(msg_type, &rlp)?.into()), } } pub fn msg_type(&self) -> String { match self { - Self::Notification(n) => format!("notification type {}", n.msg_type()), Self::Request(r) => format!("request type {}", r.msg_type()), Self::Response(r) => format!("response type {}", r.msg_type()), + Self::RelayInitNotification(n) => format!("notification type {}", n.msg_type()), + Self::RelayMsgNotification(n) => format!("notification type {}", n.msg_type()), } } } @@ -413,12 +421,13 @@ mod tests { let mut nonce = [0u8; MESSAGE_NONCE_LENGTH]; nonce[MESSAGE_NONCE_LENGTH - nonce_bytes.len()..].copy_from_slice(&nonce_bytes); - let notif = Message::Notification(Notification::RelayInit(inr_enr, tgt_node_id, nonce)); + let notif = RelayInitNotification::new(inr_enr, tgt_node_id, nonce); + let msg = Message::RelayInitNotification(notif); - let encoded_notif = notif.clone().encode(); - let decoded_notif = Message::decode(&encoded_notif).expect("Should decode"); + let encoded_msg = msg.clone().encode(); + let decoded_msg = Message::decode(&encoded_msg).expect("Should decode"); - assert_eq!(notif, decoded_notif); + assert_eq!(msg, decoded_msg); } #[test] @@ -432,11 +441,12 @@ mod tests { let mut nonce = [0u8; MESSAGE_NONCE_LENGTH]; nonce[MESSAGE_NONCE_LENGTH - nonce_bytes.len()..].copy_from_slice(&nonce_bytes); - let notif = Message::Notification(Notification::RelayMsg(inr_enr, nonce)); + let notif = RelayMsgNotification::new(inr_enr, nonce); + let msg = Message::RelayMsgNotification(notif); - let encoded_notif = notif.clone().encode(); - let decoded_notif = Message::decode(&encoded_notif).expect("Should decode"); + let encoded_msg = msg.clone().encode(); + let decoded_msg = Message::decode(&encoded_msg).expect("Should decode"); - assert_eq!(notif, decoded_notif); + assert_eq!(msg, decoded_msg); } } diff --git a/src/rpc/notification.rs b/src/rpc/notification.rs index dc29b2b22..cfae7d6b0 100644 --- a/src/rpc/notification.rs +++ b/src/rpc/notification.rs @@ -6,31 +6,41 @@ use crate::{ use derive_more::Display; use enr::NodeId; use rlp::{DecoderError, Rlp, RlpStream}; -use std::convert::TryInto; /// Nonce of request that triggered the initiation of this hole punching attempt. type NonceOfTimedOutMessage = MessageNonce; /// Node id length in bytes. pub const NODE_ID_LENGTH: usize = 32; -/// A unicast notification sent over discv5. +/// Unicast notifications [`RelayInitNotification`] and [`RelayMsgNotification`] sent over discv5. + +/// A notification to initialise a one-shot relay circuit for hole-punching. #[derive(Debug, Display, PartialEq, Eq, Clone)] -pub enum Notification { - /// A notification to initialise a one-shot relay circuit for hole-punching. - #[display(fmt = "Notification: RelayInit: Initiator: {_0}, Target: {_1}, Nonce: {_2:?}")] - RelayInit(Enr, NodeId, NonceOfTimedOutMessage), - /// The notification relayed to target of hole punch attempt. - #[display(fmt = "Notification: RelayMsg: Initiator: {_0}, Nonce: {_1:?}")] - RelayMsg(Enr, NonceOfTimedOutMessage), +#[display(fmt = "Notification: RelayInit: Initiator: {_0}, Target: {_1}, Nonce: {_2:?}")] +pub struct RelayInitNotification(Enr, NodeId, NonceOfTimedOutMessage); + +impl RelayInitNotification { + pub fn new( + initr_enr: Enr, + tgt_node_id: NodeId, + timed_out_msg_nonce: NonceOfTimedOutMessage, + ) -> Self { + Self(initr_enr, tgt_node_id, timed_out_msg_nonce) + } + + pub fn initiator_enr(&self) -> &Enr { + &self.0 + } + + pub fn target_node_id(&self) -> NodeId { + self.1 + } } -impl Payload for Notification { +impl Payload for RelayInitNotification { /// Matches a notification type to its message type id. fn msg_type(&self) -> u8 { - match self { - Self::RelayInit(..) => MessageType::RelayInit as u8, - Self::RelayMsg(..) => MessageType::RelayMsg as u8, - } + MessageType::RelayInit as u8 } /// Encodes a notification message to RLP-encoded bytes. @@ -39,71 +49,112 @@ impl Payload for Notification { let msg_type = self.msg_type(); buf.push(msg_type); let mut s = RlpStream::new(); - match self { - Self::RelayInit(initiator, target, nonce) => { - s.begin_list(3); - s.append(&initiator); - s.append(&(&target.raw() as &[u8])); - s.append(&(&nonce as &[u8])); - } - Self::RelayMsg(initiator, nonce) => { - s.begin_list(2); - s.append(&initiator); - s.append(&(&nonce as &[u8])); - } - } + let Self(initiator, target, nonce) = self; + + s.begin_list(3); + s.append(&initiator); + s.append(&(&target.raw() as &[u8])); + s.append(&(&nonce as &[u8])); + buf.extend_from_slice(&s.out()); buf } /// Decodes RLP-encoded bytes into a notification message. - fn decode(msg_type: u8, rlp: &Rlp<'_>) -> Result { - match msg_type.try_into()? { - MessageType::RelayInit => { - if rlp.item_count()? != 3 { - return Err(DecoderError::RlpIncorrectListLen); - } - let initiator = rlp.val_at::(0)?; - - let tgt_bytes = rlp.val_at::>(1)?; - if tgt_bytes.len() > NODE_ID_LENGTH { - return Err(DecoderError::RlpIsTooBig); - } - let mut tgt = [0u8; NODE_ID_LENGTH]; - tgt[NODE_ID_LENGTH - tgt_bytes.len()..].copy_from_slice(&tgt_bytes); - let tgt = NodeId::from(tgt); - - let nonce = { - let bytes = rlp.val_at::>(2)?; - if bytes.len() > MESSAGE_NONCE_LENGTH { - return Err(DecoderError::RlpIsTooBig); - } - let mut buf = [0u8; MESSAGE_NONCE_LENGTH]; - buf[MESSAGE_NONCE_LENGTH - bytes.len()..].copy_from_slice(&bytes); - buf - }; - - Ok(Notification::RelayInit(initiator, tgt, nonce)) - } - MessageType::RelayMsg => { - if rlp.item_count()? != 2 { - return Err(DecoderError::RlpIncorrectListLen); - } - let initiator = rlp.val_at::(0)?; - - let nonce = { - let bytes = rlp.val_at::>(1)?; - if bytes.len() > MESSAGE_NONCE_LENGTH { - return Err(DecoderError::RlpIsTooBig); - } - let mut buf = [0u8; MESSAGE_NONCE_LENGTH]; - buf[MESSAGE_NONCE_LENGTH - bytes.len()..].copy_from_slice(&bytes); - buf - }; - - Ok(Notification::RelayMsg(initiator, nonce)) + fn decode(_msg_type: u8, rlp: &Rlp<'_>) -> Result { + if rlp.item_count()? != 3 { + return Err(DecoderError::RlpIncorrectListLen); + } + let initiator = rlp.val_at::(0)?; + + let tgt_bytes = rlp.val_at::>(1)?; + if tgt_bytes.len() > NODE_ID_LENGTH { + return Err(DecoderError::RlpIsTooBig); + } + let mut tgt = [0u8; NODE_ID_LENGTH]; + tgt[NODE_ID_LENGTH - tgt_bytes.len()..].copy_from_slice(&tgt_bytes); + let tgt = NodeId::from(tgt); + + let nonce = { + let bytes = rlp.val_at::>(2)?; + if bytes.len() > MESSAGE_NONCE_LENGTH { + return Err(DecoderError::RlpIsTooBig); } - _ => unreachable!("Implementation does not adhere to wire protocol"), + let mut buf = [0u8; MESSAGE_NONCE_LENGTH]; + buf[MESSAGE_NONCE_LENGTH - bytes.len()..].copy_from_slice(&bytes); + buf + }; + + Ok(Self(initiator, tgt, nonce)) + } +} + +impl From for (Enr, NodeId, NonceOfTimedOutMessage) { + fn from(value: RelayInitNotification) -> Self { + let RelayInitNotification(initr_enr, tgt_node_id, timed_out_msg_nonce) = value; + + (initr_enr, tgt_node_id, timed_out_msg_nonce) + } +} + +/// The notification relayed to target of hole punch attempt. +#[derive(Debug, Display, PartialEq, Eq, Clone)] +#[display(fmt = "Notification: RelayMsg: Initiator: {_0}, Nonce: {_1:?}")] +pub struct RelayMsgNotification(Enr, NonceOfTimedOutMessage); + +impl RelayMsgNotification { + pub fn new(initr_enr: Enr, timed_out_msg_nonce: NonceOfTimedOutMessage) -> Self { + RelayMsgNotification(initr_enr, timed_out_msg_nonce) + } +} + +impl Payload for RelayMsgNotification { + /// Matches a notification type to its message type id. + fn msg_type(&self) -> u8 { + MessageType::RelayMsg as u8 + } + + /// Encodes a notification message to RLP-encoded bytes. + fn encode(self) -> Vec { + let mut buf = Vec::with_capacity(100); + let msg_type = self.msg_type(); + buf.push(msg_type); + let mut s = RlpStream::new(); + let Self(initiator, nonce) = self; + + s.begin_list(2); + s.append(&initiator); + s.append(&(&nonce as &[u8])); + + buf.extend_from_slice(&s.out()); + buf + } + + /// Decodes RLP-encoded bytes into a notification message. + fn decode(_msg_type: u8, rlp: &Rlp<'_>) -> Result { + if rlp.item_count()? != 2 { + return Err(DecoderError::RlpIncorrectListLen); } + let initiator = rlp.val_at::(0)?; + + let nonce = { + let bytes = rlp.val_at::>(1)?; + if bytes.len() > MESSAGE_NONCE_LENGTH { + return Err(DecoderError::RlpIsTooBig); + } + let mut buf = [0u8; MESSAGE_NONCE_LENGTH]; + buf[MESSAGE_NONCE_LENGTH - bytes.len()..].copy_from_slice(&bytes); + buf + }; + + Ok(Self(initiator, nonce)) + } +} + +impl From for (Enr, NonceOfTimedOutMessage) { + fn from(value: RelayMsgNotification) -> Self { + let RelayMsgNotification(initr_enr, timed_out_msg_nonce) = value; + + (initr_enr, timed_out_msg_nonce) } } diff --git a/src/service.rs b/src/service.rs index ea72e5339..0451ab0f5 100644 --- a/src/service.rs +++ b/src/service.rs @@ -409,19 +409,28 @@ impl Service { } self.rpc_failure(request_id, error); } - HandlerOut::FindHolePunchEnr(tgt_node_id, relay_msg_notif) => { - // check if we know this node id in our routing table, otherwise drop - // notification. - // todo(emhane): ban peers that ask us to relay to a peer we very - // unlikely could have sent to them in a NODES response. - let key = kbucket::Key::from(tgt_node_id); - if let kbucket::Entry::Present(entry, _) = self.kbuckets.write().entry(&key) { - let enr = entry.value().clone(); - if let Err(e) = self.handler_send.send(HandlerIn::HolePunchEnr(enr, relay_msg_notif)) { - warn!("Failed to send target enr to relay proccess, error: {}", e); + HandlerOut::FindHolePunchEnr(relay_init) => { + // check if we know the target node id in our routing table, otherwise + // drop relay attempt. + let tgt_node_id = relay_init.target_node_id(); + let tgt_key = kbucket::Key::from(tgt_node_id); + if let kbucket::Entry::Present(entry, _) = self.kbuckets.write().entry(&tgt_key) { + let tgt_enr = entry.value().clone(); + if let Err(e) = self.handler_send.send(HandlerIn::HolePunchEnr(tgt_enr, relay_init)) { + warn!( + "Failed to send target enr to relay process, error: {e}" + ); } } else { - warn!("Peer {tgt_node_id} requested relaying to a peer not in k-buckets, {relay_msg_notif}"); + // todo(emhane): ban peers that ask us to relay to a peer we very + // unlikely could have sent to them in a NODES response. + let inr_node_id = relay_init.initiator_enr().node_id(); + + warn!( + inr_node_id=%inr_node_id, + tgt_node_id=%tgt_node_id, + "Peer requested relaying to a peer not in k-buckets" + ); } } } @@ -596,8 +605,8 @@ impl Service { } } kbucket::Entry::Pending(ref mut entry, _) => { - if entry.value().seq() < enr_seq { - let enr = entry.value().clone(); + if entry.value_mut().seq() < enr_seq { + let enr = entry.value_mut().clone(); to_request_enr = Some(enr); } } @@ -855,9 +864,11 @@ impl Service { new_ip6, )); // Notify Handler of socket update - if let Err(e) = self - .handler_send - .send(HandlerIn::SocketUpdate(new_ip6)) + if let Err(e) = + self.handler_send.send(HandlerIn::SocketUpdate( + local_ip6_socket.map(SocketAddr::V6), + new_ip6, + )) { warn!("Failed to send socket update to handler: {}", e); }; @@ -881,9 +892,11 @@ impl Service { new_ip4, )); // Notify Handler of socket update - if let Err(e) = self - .handler_send - .send(HandlerIn::SocketUpdate(new_ip4)) + if let Err(e) = + self.handler_send.send(HandlerIn::SocketUpdate( + local_ip4_socket.map(SocketAddr::V4), + new_ip4, + )) { warn!("Failed to send socket update {}", e); }; @@ -1234,7 +1247,7 @@ impl Service { let must_update_enr = match self.kbuckets.write().entry(&key) { kbucket::Entry::Present(entry, _) => entry.value().seq() < enr.seq(), - kbucket::Entry::Pending(mut entry, _) => entry.value().seq() < enr.seq(), + kbucket::Entry::Pending(mut entry, _) => entry.value_mut().seq() < enr.seq(), _ => false, }; From 25077d4e919b9f5a061281f6d3c914811517a15e Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 14 Jan 2024 09:15:49 +0100 Subject: [PATCH 142/154] Reset commit b32e7507 and check sequence of initiator's enr against kbucket entry --- src/handler/mod.rs | 17 +++++++++++------ src/service.rs | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index c1c05fba9..05341669b 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -384,12 +384,17 @@ impl Handler { HandlerIn::SocketUpdate(old_socket, socket) => { let ip = socket.ip(); let port = socket.port(); - // This node goes from being unreachable to being reachable. - // Reasonably assuming all its peers are indexing sessions based on - // `node_id`, like this implementation, the first message sent in each - // session from here on will trigger a WHOAREYOU message from the peer - // (since the peer won't be able to find the decryption key for the - // session with the new node id as message's src id). + if old_socket.is_none() { + // This node goes from being unreachable to being reachable, but + // keeps the same enr key (hence same node id). Remove its + // sessions to trigger a WHOAREYOU from peers on next sent + // message. If the peer is running this implementation of + // discovery, this makes it possible for the local node to be + // inserted into its peers' kbuckets before the session they + // already had expires. Session duration, in this impl defaults to + // 24 hours. + self.sessions.cache.clear() + } self.nat_hole_puncher.set_is_behind_nat(self.listen_sockets.iter(), Some(ip), Some(port)); } } diff --git a/src/service.rs b/src/service.rs index 0451ab0f5..1bb246442 100644 --- a/src/service.rs +++ b/src/service.rs @@ -410,6 +410,24 @@ impl Service { self.rpc_failure(request_id, error); } HandlerOut::FindHolePunchEnr(relay_init) => { + // update initiator's enr if it's in kbuckets + let inr_enr = relay_init.initiator_enr(); + let inr_key = kbucket::Key::from(inr_enr.node_id()); + match self.kbuckets.write().entry(&inr_key) { + kbucket::Entry::Present(ref mut entry, _) => { + let enr = entry.value_mut(); + if enr.seq() < inr_enr.seq() { + *enr = inr_enr.clone(); + } + } + kbucket::Entry::Pending(ref mut entry, _) => { + let enr = entry.value_mut(); + if enr.seq() < inr_enr.seq() { + *enr = inr_enr.clone(); + } + } + _ => () + } // check if we know the target node id in our routing table, otherwise // drop relay attempt. let tgt_node_id = relay_init.target_node_id(); From 41f0ef64dbf6fa24745787d8e5ad889132d93ba5 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 14 Jan 2024 09:16:30 +0100 Subject: [PATCH 143/154] Refactor initiator abbreviation --- src/handler/mod.rs | 16 +++++++------- src/handler/tests.rs | 52 ++++++++++++++++++++++---------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 05341669b..6c9f1e713 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -375,8 +375,8 @@ impl Handler { HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge::

(wru_ref, enr).await, HandlerIn::HolePunchEnr(tgt_enr, relay_init) => { // Assemble the notification for the target - let (initr_enr, _tgt, timed_out_nonce) = relay_init.into(); - let relay_msg_notif = RelayMsgNotification::new(initr_enr, timed_out_nonce); + let (inr_enr, _tgt, timed_out_nonce) = relay_init.into(); + let relay_msg_notif = RelayMsgNotification::new(inr_enr, timed_out_nonce); if let Err(e) = self.send_relay_msg_notif::

(tgt_enr, relay_msg_notif).await { warn!("Failed to relay. Error: {}", e); } @@ -1094,9 +1094,9 @@ impl Handler { match message { Message::Response(response) => self.handle_response::

(node_address, response).await, Message::RelayInitNotification(notif) => { - let initr_node_id = notif.initiator_enr().node_id(); - if initr_node_id != node_address.node_id { - warn!("peer {node_address} tried to initiate hole punch attempt for another node {initr_node_id}, banning peer {node_address}"); + let inr_node_id = notif.initiator_enr().node_id(); + if inr_node_id != node_address.node_id { + warn!("peer {node_address} tried to initiate hole punch attempt for another node {inr_node_id}, banning peer {node_address}"); self.fail_session(&node_address, RequestError::MaliciousRelayInit, true) .await; let ban_timeout = self @@ -1111,7 +1111,7 @@ impl Handler { Message::RelayMsgNotification(notif) => { match self.nat_hole_puncher.is_behind_nat { Some(false) => { - // initr may not be malicious and initiated a hole punch attempt when + // inr may not be malicious and initiated a hole punch attempt when // a request to this node timed out for another reason debug!("peer {node_address} relayed a hole punch notification but we are not behind nat"); } @@ -1590,9 +1590,9 @@ impl HolePunchNat for Handler { &mut self, relay_msg: RelayMsgNotification, ) -> Result<(), NatHolePunchError> { - let (initr_enr, timed_out_msg_nonce) = relay_msg.into(); + let (inr_enr, timed_out_msg_nonce) = relay_msg.into(); let initiator_node_address = - match NodeContact::try_from_enr(initr_enr, self.nat_hole_puncher.ip_mode) { + match NodeContact::try_from_enr(inr_enr, self.nat_hole_puncher.ip_mode) { Ok(contact) => contact.node_address(), Err(e) => return Err(NatHolePunchError::Target(e.into())), }; diff --git a/src/handler/tests.rs b/src/handler/tests.rs index 67d3b5f9c..bc38ad694 100644 --- a/src/handler/tests.rs +++ b/src/handler/tests.rs @@ -497,7 +497,7 @@ async fn nat_hole_punch_relay() { let mut dummy_session = build_dummy_session(); // Initiator - let initr_enr = { + let inr_enr = { let key = CombinedKey::generate_secp256k1(); EnrBuilder::new("v4") .ip4(Ipv4Addr::LOCALHOST) @@ -505,16 +505,16 @@ async fn nat_hole_punch_relay() { .build(&key) .unwrap() }; - let initr_addr = initr_enr.udp4_socket().unwrap().into(); - let initr_node_id = initr_enr.node_id(); + let inr_addr = inr_enr.udp4_socket().unwrap().into(); + let inr_node_id = inr_enr.node_id(); - let initr_node_address = NodeAddress::new(initr_addr, initr_enr.node_id()); + let inr_node_address = NodeAddress::new(inr_addr, inr_enr.node_id()); handler .sessions .cache - .insert(initr_node_address, dummy_session.clone()); + .insert(inr_node_address, dummy_session.clone()); - let initr_socket = UdpSocket::bind(initr_addr) + let inr_socket = UdpSocket::bind(inr_addr) .await .expect("should bind to initiator socket"); @@ -559,16 +559,16 @@ async fn nat_hole_punch_relay() { // Initiator handle let relay_init_notif = - RelayInitNotification::new(initr_enr.clone(), tgt_node_id, MessageNonce::default()); + RelayInitNotification::new(inr_enr.clone(), tgt_node_id, MessageNonce::default()); - let initr_handle = tokio::spawn(async move { + let inr_handle = tokio::spawn(async move { let mut session = build_dummy_session(); let packet = session - .encrypt_session_message::(initr_node_id, &relay_init_notif.encode()) + .encrypt_session_message::(inr_node_id, &relay_init_notif.encode()) .expect("should encrypt notification"); let encoded_packet = packet.encode::(&relay_node_id); - initr_socket + inr_socket .send_to(&encoded_packet, relay_addr) .await .expect("should relay init notification to relay") @@ -589,10 +589,10 @@ async fn nat_hole_punch_relay() { }); // Join all handles - let (initr_res, relay_res, tgt_res, mock_service_res) = - tokio::join!(initr_handle, relay_handle, tgt_handle, mock_service_handle); + let (inr_res, relay_res, tgt_res, mock_service_res) = + tokio::join!(inr_handle, relay_handle, tgt_handle, mock_service_handle); - initr_res.unwrap(); + inr_res.unwrap(); relay_res.unwrap(); mock_service_res.unwrap(); @@ -624,7 +624,7 @@ async fn nat_hole_punch_relay() { match Message::decode(&decrypted_message).expect("should decode message") { Message::RelayMsgNotification(relay_msg) => { let (enr, _) = relay_msg.into(); - assert_eq!(initr_enr, enr) + assert_eq!(inr_enr, enr) } _ => panic!("message should decode to a relay msg notification"), } @@ -666,7 +666,7 @@ async fn nat_hole_punch_target() { .expect("should bind to target socket"); // Initiator - let initr_enr = { + let inr_enr = { let key = CombinedKey::generate_secp256k1(); EnrBuilder::new("v4") .ip4(Ipv4Addr::LOCALHOST) @@ -674,11 +674,11 @@ async fn nat_hole_punch_target() { .build(&key) .unwrap() }; - let initr_addr = initr_enr.udp4_socket().unwrap(); - let initr_node_id = initr_enr.node_id(); - let initr_nonce: MessageNonce = [1; MESSAGE_NONCE_LENGTH]; + let inr_addr = inr_enr.udp4_socket().unwrap(); + let inr_node_id = inr_enr.node_id(); + let inr_nonce: MessageNonce = [1; MESSAGE_NONCE_LENGTH]; - let initr_socket = UdpSocket::bind(initr_addr) + let inr_socket = UdpSocket::bind(inr_addr) .await .expect("should bind to initiator socket"); @@ -686,7 +686,7 @@ async fn nat_hole_punch_target() { let tgt_handle = tokio::spawn(async move { handler.start::().await }); // Relay handle - let relay_msg_notif = RelayMsgNotification::new(initr_enr.clone(), initr_nonce); + let relay_msg_notif = RelayMsgNotification::new(inr_enr.clone(), inr_nonce); let relay_handle = tokio::spawn(async move { let mut session = build_dummy_session(); @@ -703,9 +703,9 @@ async fn nat_hole_punch_target() { // Initiator handle let target_exit = mock_service.exit_tx; - let initr_handle = tokio::spawn(async move { + let inr_handle = tokio::spawn(async move { let mut buffer = [0; MAX_PACKET_SIZE]; - let res = initr_socket + let res = inr_socket .recv_from(&mut buffer) .await .expect("should read bytes from socket"); @@ -716,16 +716,16 @@ async fn nat_hole_punch_target() { }); // Join all handles - let (tgt_res, relay_res, initr_res) = tokio::join!(tgt_handle, relay_handle, initr_handle); + let (tgt_res, relay_res, inr_res) = tokio::join!(tgt_handle, relay_handle, inr_handle); tgt_res.unwrap(); relay_res.unwrap(); - let ((length, src), buffer) = initr_res.unwrap(); + let ((length, src), buffer) = inr_res.unwrap(); assert_eq!(src, tgt_addr); - let (packet, _aad) = Packet::decode::(&initr_node_id, &buffer[..length]) + let (packet, _aad) = Packet::decode::(&inr_node_id, &buffer[..length]) .expect("should decode packet"); let Packet { header, .. } = packet; let PacketHeader { @@ -735,5 +735,5 @@ async fn nat_hole_punch_target() { } = header; assert!(kind.is_whoareyou()); - assert_eq!(message_nonce, initr_nonce) + assert_eq!(message_nonce, inr_nonce) } From 61875972eeeb858b3c20a18032ada604d46fd9ee Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 15 Jan 2024 04:31:10 +0100 Subject: [PATCH 144/154] Trigger ping all peers on upgrade to reachable enr --- src/handler/mod.rs | 18 +++++++++++++++++- src/service.rs | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 6c9f1e713..bcdf97042 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -150,6 +150,15 @@ pub enum HandlerOut { /// Look-up an ENR in k-buckets. Passes the node id of the peer to look up and the /// [`RelayMsgNotification`] we intend to send to it. FindHolePunchEnr(RelayInitNotification), + + /// Triggers a ping to all peers, outside of the regular ping interval. Needed to trigger + /// renewed session establishment after updating the local ENR from unreachable to reachable + /// and clearing all sessions. Only this way does the local node have a chance to make it into + /// its peers kbuckets before the session expires (defaults to 24 hours). This is the case + /// since its peers, running this implementation, will only respond to PINGs from nodes in its + /// kbucktes and unreachable ENRs don't make it into kbuckets upon [`HandlerOut::Established`] + /// event. + PingAllPeers, } /// How we connected to the node. @@ -393,7 +402,14 @@ impl Handler { // inserted into its peers' kbuckets before the session they // already had expires. Session duration, in this impl defaults to // 24 hours. - self.sessions.cache.clear() + self.sessions.cache.clear(); + if let Err(e) = self + .service_send + .send(HandlerOut::PingAllPeers) + .await + { + warn!("Failed to inform that request failed {}", e); + } } self.nat_hole_puncher.set_is_behind_nat(self.listen_sockets.iter(), Some(ip), Some(port)); } diff --git a/src/service.rs b/src/service.rs index 1bb246442..783d175ca 100644 --- a/src/service.rs +++ b/src/service.rs @@ -451,6 +451,7 @@ impl Service { ); } } + HandlerOut::PingAllPeers => self.ping_connected_peers() } } event = Service::bucket_maintenance_poll(&self.kbuckets) => { From 8fd543429029aa4d6c5c2f688a5733e4a59fb8ce Mon Sep 17 00:00:00 2001 From: Age Manning Date: Mon, 15 Jan 2024 18:42:13 +1100 Subject: [PATCH 145/154] Modify session management to inside the lrutimecache --- src/config.rs | 10 +- src/handler/{sessions => }/crypto/ecdh.rs | 0 src/handler/{sessions => }/crypto/mod.rs | 0 src/handler/mod.rs | 201 +++++++++++++--------- src/handler/nat_hole_punch/mod.rs | 2 +- src/handler/nat_hole_punch/utils.rs | 59 ++----- src/handler/{sessions => }/session.rs | 99 ++--------- src/handler/sessions/limiter.rs | 69 -------- src/handler/sessions/mod.rs | 121 ------------- src/handler/tests.rs | 18 +- src/lru_time_cache.rs | 116 +++++++++---- src/packet/mod.rs | 2 +- 12 files changed, 241 insertions(+), 456 deletions(-) rename src/handler/{sessions => }/crypto/ecdh.rs (100%) rename src/handler/{sessions => }/crypto/mod.rs (100%) rename src/handler/{sessions => }/session.rs (75%) delete mode 100644 src/handler/sessions/limiter.rs delete mode 100644 src/handler/sessions/mod.rs diff --git a/src/config.rs b/src/config.rs index 1834b462c..0d3fafa3b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,9 +1,13 @@ //! A set of configuration parameters to tune the discovery protocol. use crate::{ - handler::MIN_SESSIONS_UNREACHABLE_ENR, kbucket::MAX_NODES_PER_BUCKET, Enr, Executor, - ListenConfig, PermitBanList, RateLimiter, RateLimiterBuilder, + kbucket::MAX_NODES_PER_BUCKET, Enr, Executor, ListenConfig, PermitBanList, RateLimiter, + RateLimiterBuilder, }; +/// The minimum number of unreachable Sessions a node must allow. This enables the network to +/// boostrap. +const MIN_SESSIONS_UNREACHABLE_ENR: usize = 10; + use std::{ops::RangeInclusive, time::Duration}; /// Configuration parameters that define the performance of the discovery network. @@ -100,7 +104,7 @@ pub struct Discv5Config { /// The max limit for peers with unreachable ENRs. Benevolent examples of such peers are peers /// that are discovering their externally reachable socket, nodes must assist at least one /// such peer in discovering their reachable socket via ip voting, and peers behind symmetric - /// NAT. Default is no limit. Minimum is 1. + /// NAT. Default is no limit. Minimum is 10. pub unreachable_enr_limit: Option, /// The unused port range to try and bind to when testing if this node is behind NAT based on diff --git a/src/handler/sessions/crypto/ecdh.rs b/src/handler/crypto/ecdh.rs similarity index 100% rename from src/handler/sessions/crypto/ecdh.rs rename to src/handler/crypto/ecdh.rs diff --git a/src/handler/sessions/crypto/mod.rs b/src/handler/crypto/mod.rs similarity index 100% rename from src/handler/sessions/crypto/mod.rs rename to src/handler/crypto/mod.rs diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 301e72344..2a72625c3 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -59,18 +59,18 @@ use tracing::{debug, error, trace, warn}; mod active_requests; mod nat_hole_punch; mod request_call; -mod sessions; +mod session; +mod crypto; mod tests; use crate::metrics::METRICS; pub use crate::node_info::{NodeAddress, NodeContact}; -pub use sessions::MIN_SESSIONS_UNREACHABLE_ENR; use crate::{lru_time_cache::LruTimeCache, socket::ListenConfig}; use active_requests::ActiveRequests; -use nat_hole_punch::{Error as NatHolePunchError, HolePunchNat, NatHolePunchUtils}; +use nat_hole_punch::{Error as NatError, HolePunchNat, NatUtils}; use request_call::RequestCall; -use sessions::{Session, Sessions}; +use session::Session; // The time interval to check banned peer timeouts and unban peers when the timeout has elapsed (in // seconds). @@ -210,7 +210,7 @@ pub struct Handler { /// Currently in-progress outbound handshakes (WHOAREYOU packets) with peers. active_challenges: HashMapDelay, /// Established sessions with peers. - sessions: Sessions, + sessions: LruTimeCache, /// Established sessions with peers for a specific request, stored just one per node. one_time_sessions: LruTimeCache, /// The channel to receive messages from the application layer. @@ -224,7 +224,7 @@ pub struct Handler { /// Exit channel to shutdown the handler. exit: oneshot::Receiver<()>, /// Types necessary to plug in nat hole punching. - nat_hole_puncher: NatHolePunchUtils, + nat_utils: NatUtils, } type HandlerReturn = ( @@ -307,19 +307,19 @@ impl Handler { // Attempt to bind to the socket before spinning up the send/recv tasks. let socket = Socket::new::

(socket_config).await?; - let sessions = Sessions::new( - session_cache_capacity, + let sessions = LruTimeCache::new( session_timeout, - unreachable_enr_limit, + Some(session_cache_capacity), ); - let nat_hole_puncher = NatHolePunchUtils::new( + let nat_utils = NatUtils::new( listen_sockets.iter(), &enr.read(), ip_mode, unused_port_range, ban_duration, session_cache_capacity, + unreachable_enr_limit, ); executor @@ -343,7 +343,7 @@ impl Handler { service_send, listen_sockets, socket, - nat_hole_puncher, + nat_utils, exit, }; debug!("Handler Starting"); @@ -388,9 +388,9 @@ impl Handler { // inserted into its peers' kbuckets before the session they // already had expires. Session duration, in this impl defaults to // 24 hours. - self.sessions.cache.clear() + self.sessions.clear() } - self.nat_hole_puncher.set_is_behind_nat(self.listen_sockets.iter(), Some(ip), Some(port)); + self.nat_utils.set_is_behind_nat(self.listen_sockets.iter(), Some(ip), Some(port)); } } } @@ -405,7 +405,12 @@ impl Handler { // challenge. We process them here self.send_next_request::

(node_address).await; } - Some(peer_socket) = self.nat_hole_puncher.next() => { + Some(Ok(peer_socket)) = self.nat_utils.hole_punch_tracker.next() => { + if self.nat_utils.is_behind_nat == Some(false) { + // Until ip voting is done and an observed public address is finalised, all nodes act as + // if they are behind a NAT. + return; + } if let Err(e) = self.on_hole_punch_expired(peer_socket).await { warn!("Failed to keep hole punched for peer, error: {}", e); } @@ -516,7 +521,7 @@ impl Handler { if request_call.retries() >= self.request_retries { trace!("Request timed out with {}", node_address); if let Some(relay) = self - .nat_hole_puncher + .nat_utils .new_peer_latest_relay_cache .pop(&node_address.node_id) { @@ -590,7 +595,7 @@ impl Handler { } let (packet, initiating_session) = { - if let Some(session) = self.sessions.cache.get_mut(&node_address) { + if let Some(session) = self.sessions.get_mut(&node_address) { // Encrypt the message and send let request = match &request_id { HandlerReqId::Internal(id) | HandlerReqId::External(id) => Request { @@ -637,7 +642,7 @@ impl Handler { response: Response, ) { // Check for an established session - let packet = if let Some(session) = self.sessions.cache.get_mut(&node_address) { + let packet = if let Some(session) = self.sessions.get_mut(&node_address) { session.encrypt_session_message::

(self.node_id, &response.encode()) } else if let Some(mut session) = self.remove_one_time_session(&node_address, &response.id) { @@ -797,6 +802,10 @@ impl Handler { // All sent requests must have an associated node_id. Therefore the following // must not panic. let node_address = request_call.contact().node_address(); + + // Keep track if the ENR is reachable. In the case we don't know the ENR, we assume its + // fine. + let mut enr_not_reachable = false; match request_call.contact().enr() { Some(enr) => { // NOTE: Here we decide if the session is outgoing or ingoing. The condition for an @@ -809,6 +818,8 @@ impl Handler { ConnectionDirection::Incoming }; + enr_not_reachable = NatUtils::is_enr_reachable(&enr); + // We already know the ENR. Send the handshake response packet trace!("Sending Authentication response to node: {}", node_address); request_call.update_packet(auth_packet.clone()); @@ -846,7 +857,7 @@ impl Handler { } } } - self.new_session(node_address, session); + self.new_session(node_address, session, enr_not_reachable); } /// Verifies a Node ENR to it's observed address. If it fails, any associated session is also @@ -886,20 +897,37 @@ impl Handler { ); if let Some(challenge) = self.active_challenges.remove(&node_address) { - let session_limiter = self.sessions.limiter.as_mut(); + + // Find the most recent ENR, a known ENR or one they sent in their challenge. + let Challenge { data, remote_enr } = challenge; + let Ok(most_recent_enr) = most_recent_enr(enr_record, remote_enr) else { + warn!( + "Peer did not respond with their ENR. Session could not be established. Node: {}",node_address + ); + self.fail_session(&node_address, RequestError::InvalidRemotePacket, true) + .await; + return; + }; + + // Keep count of the unreachable Sessions we are tracking + // Peer is reachable + let enr_not_reachable = !NatUtils::is_enr_reachable(&most_recent_enr); + + + // Decide whether to establish this connection based on our apettiite for unreachable + if enr_not_reachable && Some(self.sessions.tagged()) > self.nat_utils.unreachable_enr_limit { + debug!("Reached limit of unreachable ENR sessions. Avoiding a new connection. Limit: {}", self.sessions.tagged()); + return; + } match Session::establish_from_challenge( self.key.clone(), &self.node_id, - challenge, + &node_address.node_id, + data, id_nonce_sig, ephem_pubkey, - enr_record, - &node_address, - |node_address, enr| { - session_limiter - .map(|limiter| limiter.track_sessions_unreachable_enr(node_address, enr)) - }, + most_recent_enr, ) { Ok((mut session, enr)) => { // Receiving an AuthResponse must give us an up-to-date view of the node ENR. @@ -915,8 +943,8 @@ impl Handler { ConnectionDirection::Incoming, ) .await; - self.new_session(node_address.clone(), session); - self.nat_hole_puncher + self.new_session(node_address.clone(), session, enr_not_reachable); + self.nat_utils .new_peer_latest_relay_cache .pop(&node_address.node_id); self.handle_message::

( @@ -981,9 +1009,6 @@ impl Handler { // insert back the challenge self.active_challenges.insert(node_address, challenge); } - Err(Discv5Error::LimitSessionsUnreachableEnr) => { - warn!("Limit reached for sessions with unreachable ENRs. Dropping session."); - } Err(e) => { warn!( "Invalid Authentication header. Dropping session. Error: {:?}", @@ -1052,7 +1077,7 @@ impl Handler { authenticated_data: &[u8], ) { // check if we have an available session - let Some(session) = self.sessions.cache.get_mut(&node_address) else { + let Some(session) = self.sessions.get_mut(&node_address) else { warn!( "Dropping message. Error: {}, {}", Discv5Error::SessionNotEstablished, @@ -1088,31 +1113,31 @@ impl Handler { match message { Message::Response(response) => self.handle_response::

(node_address, response).await, - Message::Notification(notif) => match notif { - Notification::RelayInit(initr, tgt, timed_out_nonce) => { - let initr_node_id = initr.node_id(); - if initr_node_id != node_address.node_id { - warn!("peer {node_address} tried to initiate hole punch attempt for another node {initr_node_id}, banning peer {node_address}"); + Message::Notification(notification) => match notification { + Notification::RelayInit(initiator, target, timed_out_nonce) => { + let initiator_node_id = initiator.node_id(); + if initiator_node_id != node_address.node_id { + warn!("peer {node_address} tried to initiate hole punch attempt for another node {initiator_node_id}, banning peer {node_address}"); self.fail_session(&node_address, RequestError::MaliciousRelayInit, true) .await; let ban_timeout = self - .nat_hole_puncher + .nat_utils .ban_duration .map(|v| Instant::now() + v); PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); - } else if let Err(e) = self.on_relay_init(initr, tgt, timed_out_nonce).await { + } else if let Err(e) = self.on_relay_init(initiator, target, timed_out_nonce).await { warn!("failed handling notification to relay for {node_address}, {e}"); } } - Notification::RelayMsg(initr, timed_out_nonce) => { - match self.nat_hole_puncher.is_behind_nat { + Notification::RelayMsg(initiator, timed_out_nonce) => { + match self.nat_utils.is_behind_nat { Some(false) => { - // initr may not be malicious and initiated a hole punch attempt when + // The iniator may not be malicious and initiated a hole punch attempt when // a request to this node timed out for another reason - debug!("peer {node_address} relayed a hole punch notification but we are not behind nat"); + debug!("peer {node_address} relayed a hole punch notification but we are not behind Nat"); } _ => { - if let Err(e) = self.on_relay_msg(initr, timed_out_nonce).await { + if let Err(e) = self.on_relay_msg(initiator, timed_out_nonce).await { warn!( "failed handling notification relayed from {node_address}, {e}" ); @@ -1140,7 +1165,7 @@ impl Handler { authenticated_data: &[u8], ) { // check if we have an available session - if let Some(session) = self.sessions.cache.get_mut(&node_address) { + if let Some(session) = self.sessions.get_mut(&node_address) { // attempt to decrypt and process the message. let message = match session.decrypt_message(message_nonce, message, authenticated_data) { @@ -1236,7 +1261,7 @@ impl Handler { // Sessions could be awaiting an ENR response. Check if this response matches // this // check if we have an available session - let Some(session) = self.sessions.cache.get_mut(&node_address) else { + let Some(session) = self.sessions.get_mut(&node_address) else { warn!( "Dropping response. Error: {}, {}", Discv5Error::SessionNotEstablished, @@ -1298,15 +1323,15 @@ impl Handler { if let ResponseBody::Nodes { total, ref nodes } = response.body { for node in nodes { if let Some(socket_addr) = - self.nat_hole_puncher.ip_mode.get_contactable_addr(node) + self.nat_utils.ip_mode.get_contactable_addr(node) { let node_id = node.node_id(); let new_peer_node_address = NodeAddress { socket_addr, node_id, }; - if self.sessions.cache.peek(&new_peer_node_address).is_none() { - self.nat_hole_puncher + if self.sessions.peek(&new_peer_node_address).is_none() { + self.nat_utils .new_peer_latest_relay_cache .put(node_id, node_address.clone()); } @@ -1378,14 +1403,15 @@ impl Handler { self.active_requests.insert(node_address, request_call); } - fn new_session(&mut self, node_address: NodeAddress, session: Session) { - if let Some(current_session) = self.sessions.cache.get_mut(&node_address) { + /// Updates the session cache for a new session. + fn new_session(&mut self, node_address: NodeAddress, session: Session, enr_not_reachable: bool) { + if let Some(current_session) = self.sessions.get_mut(&node_address) { current_session.update(session); } else { - self.sessions.cache.insert(node_address, session); + self.sessions.insert_raw(node_address, session, enr_not_reachable); METRICS .active_sessions - .store(self.sessions.cache.len(), Ordering::Relaxed); + .store(self.sessions.len(), Ordering::Relaxed); } } @@ -1431,7 +1457,7 @@ impl Handler { } } let node_address = request_call.contact().node_address(); - self.nat_hole_puncher + self.nat_utils .new_peer_latest_relay_cache .pop(&node_address.node_id); self.fail_session(&node_address, error, remove_session) @@ -1446,16 +1472,12 @@ impl Handler { remove_session: bool, ) { if remove_session { - self.sessions.cache.remove(node_address); + self.sessions.remove(node_address); METRICS .active_sessions - .store(self.sessions.cache.len(), Ordering::Relaxed); + .store(self.sessions.len(), Ordering::Relaxed); // stop keeping hole punched for peer - self.nat_hole_puncher.untrack(&node_address.socket_addr); - // update unreachable enr session limiter - if let Some(ref mut limiter) = self.sessions.limiter { - limiter.untrack_session(node_address) - } + self.nat_utils.untrack(&node_address.socket_addr); } if let Some(to_remove) = self.pending_requests.remove(node_address) { for PendingRequest { request_id, .. } in to_remove { @@ -1492,7 +1514,7 @@ impl Handler { if let Err(e) = self.socket.send.send(packet).await { warn!("Failed to send outbound packet {}", e) } - self.nat_hole_puncher.track(dst); + self.nat_utils.track(dst); } /// Check if any banned nodes have served their time and unban them. @@ -1526,6 +1548,25 @@ impl Handler { } } + +/// Given two optional ENRs, find the most recent one based on the sequence number. +/// This function will error if both inputs are None. +fn most_recent_enr(first: Option, second: Option) -> Result { + match (first, second) { + (Some(first_enr), Some(second_enr)) => { + if first_enr.seq() > second_enr.seq() { + Ok(first_enr) + } else { + Ok(second_enr) + } + } + (Some(first), None) => Ok(first), + (None, Some(second)) => Ok(second), + (None, None) => Err(()), // No ENR provided + } +} + + #[async_trait::async_trait] impl HolePunchNat for Handler { async fn on_request_time_out( @@ -1534,12 +1575,12 @@ impl HolePunchNat for Handler { local_enr: Enr, // initiator-enr timed_out_nonce: MessageNonce, target_node_address: NodeAddress, - ) -> Result<(), NatHolePunchError> { + ) -> Result<(), NatError> { // Another hole punch process with this target may have just completed. - if self.sessions.cache.get(&target_node_address).is_some() { + if self.sessions.get(&target_node_address).is_some() { return Ok(()); } - if let Some(session) = self.sessions.cache.get_mut(&relay) { + if let Some(session) = self.sessions.get_mut(&relay) { let relay_init_notif = Notification::RelayInit(local_enr, target_node_address.node_id, timed_out_nonce); trace!( @@ -1553,7 +1594,7 @@ impl HolePunchNat for Handler { { Ok(packet) => packet, Err(e) => { - return Err(NatHolePunchError::Initiator(e)); + return Err(NatError::Initiator(e)); } }; self.send(relay, packet).await; @@ -1575,7 +1616,7 @@ impl HolePunchNat for Handler { initr: Enr, tgt: NodeId, timed_out_nonce: MessageNonce, - ) -> Result<(), NatHolePunchError> { + ) -> Result<(), NatError> { // Assemble the notification for the target let relay_msg_notif = Notification::RelayMsg(initr, timed_out_nonce); @@ -1585,7 +1626,7 @@ impl HolePunchNat for Handler { .send(HandlerOut::FindHolePunchEnr(tgt, relay_msg_notif)) .await { - return Err(NatHolePunchError::Relay(e.into())); + return Err(NatError::Relay(e.into())); } Ok(()) } @@ -1594,15 +1635,15 @@ impl HolePunchNat for Handler { &mut self, initr: Enr, timed_out_nonce: MessageNonce, - ) -> Result<(), NatHolePunchError> { + ) -> Result<(), NatError> { let initiator_node_address = - match NodeContact::try_from_enr(initr, self.nat_hole_puncher.ip_mode) { + match NodeContact::try_from_enr(initr, self.nat_utils.ip_mode) { Ok(contact) => contact.node_address(), - Err(e) => return Err(NatHolePunchError::Target(e.into())), + Err(e) => return Err(NatError::Target(e.into())), }; // A session may already have been established. - if self.sessions.cache.get(&initiator_node_address).is_some() { + if self.sessions.get(&initiator_node_address).is_some() { trace!( "Session already established with initiator: {}", initiator_node_address @@ -1629,7 +1670,7 @@ impl HolePunchNat for Handler { .send(HandlerOut::WhoAreYou(whoareyou_ref)) .await { - return Err(NatHolePunchError::Target(e.into())); + return Err(NatError::Target(e.into())); } Ok(()) } @@ -1638,13 +1679,13 @@ impl HolePunchNat for Handler { &mut self, tgt_enr: Enr, relay_msg_notif: Notification, - ) -> Result<(), NatHolePunchError> { + ) -> Result<(), NatError> { let tgt_node_address = - match NodeContact::try_from_enr(tgt_enr, self.nat_hole_puncher.ip_mode) { + match NodeContact::try_from_enr(tgt_enr, self.nat_utils.ip_mode) { Ok(contact) => contact.node_address(), - Err(e) => return Err(NatHolePunchError::Relay(e.into())), + Err(e) => return Err(NatError::Relay(e.into())), }; - if let Some(session) = self.sessions.cache.get_mut(&tgt_node_address) { + if let Some(session) = self.sessions.get_mut(&tgt_node_address) { trace!( "Sending notif to target {}. relay msg: {}", tgt_node_address.node_id, @@ -1656,7 +1697,7 @@ impl HolePunchNat for Handler { { Ok(packet) => packet, Err(e) => { - return Err(NatHolePunchError::Relay(e)); + return Err(NatError::Relay(e)); } }; self.send(tgt_node_address, packet).await; @@ -1667,12 +1708,12 @@ impl HolePunchNat for Handler { // time out of the udp entrypoint for the target peer in the initiator's NAT, set by // the original timed out FINDNODE request from the initiator, as the initiator may // also be behind a NAT. - Err(NatHolePunchError::Relay(Discv5Error::SessionNotEstablished)) + Err(NatError::Relay(Discv5Error::SessionNotEstablished)) } } #[inline] - async fn on_hole_punch_expired(&mut self, peer: SocketAddr) -> Result<(), NatHolePunchError> { + async fn on_hole_punch_expired(&mut self, peer: SocketAddr) -> Result<(), NatError> { self.send_outbound(peer.into()).await; Ok(()) } diff --git a/src/handler/nat_hole_punch/mod.rs b/src/handler/nat_hole_punch/mod.rs index e59166bce..eaf0d4643 100644 --- a/src/handler/nat_hole_punch/mod.rs +++ b/src/handler/nat_hole_punch/mod.rs @@ -10,7 +10,7 @@ mod error; mod utils; pub use error::Error; -pub use utils::NatHolePunchUtils; +pub use utils::NatUtils; #[async_trait::async_trait] pub trait HolePunchNat { diff --git a/src/handler/nat_hole_punch/utils.rs b/src/handler/nat_hole_punch/utils.rs index 165e3add8..d3ed4d724 100644 --- a/src/handler/nat_hole_punch/utils.rs +++ b/src/handler/nat_hole_punch/utils.rs @@ -1,18 +1,15 @@ use std::{ net::{IpAddr, SocketAddr, UdpSocket}, ops::RangeInclusive, - pin::Pin, - task::{Context, Poll}, time::Duration, }; -use derive_more::{Deref, DerefMut}; +use delay_map::HashSetDelay; use enr::NodeId; -use futures::{channel::mpsc, Stream, StreamExt}; use lru::LruCache; use rand::Rng; -use crate::{lru_time_cache::LruTimeCache, node_info::NodeAddress, Enr, IpMode}; +use crate::{node_info::NodeAddress, Enr, IpMode}; /// The expected shortest lifetime in most NAT configurations of a punched hole in seconds. pub const DEFAULT_HOLE_PUNCH_LIFETIME: u64 = 20; @@ -22,7 +19,7 @@ pub const PORT_BIND_TRIES: usize = 4; pub const USER_AND_DYNAMIC_PORTS: RangeInclusive = 1025..=u16::MAX; /// Aggregates types necessary to implement nat hole punching for [`crate::handler::Handler`]. -pub struct NatHolePunchUtils { +pub struct NatUtils { /// Ip mode as set in config. pub ip_mode: IpMode, /// This node has been observed to be behind a NAT. @@ -36,14 +33,16 @@ pub struct NatHolePunchUtils { pub new_peer_latest_relay_cache: LruCache, /// Keeps track if this node needs to send a packet to a peer in order to keep a hole punched /// for it in its NAT. - hole_punch_tracker: NatHolePunchTracker, + pub hole_punch_tracker: HashSetDelay, /// Ports to trie to bind to check if this node is behind NAT. pub unused_port_range: Option>, /// If the filter is enabled this sets the default timeout for bans enacted by the filter. pub ban_duration: Option, + /// The number of unreachable ENRs we store at most in our session cache. + pub unreachable_enr_limit: Option, } -impl NatHolePunchUtils { +impl NatUtils { pub fn new<'a>( listen_sockets: impl Iterator, local_enr: &Enr, @@ -51,14 +50,16 @@ impl NatHolePunchUtils { unused_port_range: Option>, ban_duration: Option, session_cache_capacity: usize, + unreachable_enr_limit: Option, ) -> Self { - let mut nat_hole_puncher = NatHolePunchUtils { + let mut nat_hole_puncher = NatUtils { ip_mode, is_behind_nat: None, new_peer_latest_relay_cache: LruCache::new(session_cache_capacity), - hole_punch_tracker: NatHolePunchTracker::new(session_cache_capacity), + hole_punch_tracker: HashSetDelay::new(Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME)), unused_port_range, ban_duration, + unreachable_enr_limit, }; // Optimistically only test one advertised socket, ipv4 has precedence. If it is // reachable, assumption is made that also the other ip version socket is reachable. @@ -86,7 +87,7 @@ impl NatHolePunchUtils { if self.is_behind_nat == Some(false) { return; } - self.hole_punch_tracker.insert(peer_socket, ()); + self.hole_punch_tracker.insert(peer_socket); } pub fn untrack(&mut self, peer_socket: &SocketAddr) { @@ -121,40 +122,10 @@ impl NatHolePunchUtils { } }); } -} - -impl Stream for NatHolePunchUtils { - type Item = SocketAddr; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - // Until ip voting is done and an observed public address is finalised, all nodes act as - // if they are behind a NAT. - if self.is_behind_nat == Some(false) || self.hole_punch_tracker.len() == 0 { - return Poll::Pending; - } - self.hole_punch_tracker.expired_entries.poll_next_unpin(cx) - } -} - -#[derive(Deref, DerefMut)] -struct NatHolePunchTracker { - #[deref] - #[deref_mut] - cache: LruTimeCache, - expired_entries: mpsc::Receiver, -} - -impl NatHolePunchTracker { - fn new(session_cache_capacity: usize) -> Self { - let (tx, rx) = futures::channel::mpsc::channel::(session_cache_capacity); - Self { - cache: LruTimeCache::new_with_expiry_feedback( - Duration::from_secs(DEFAULT_HOLE_PUNCH_LIFETIME), - Some(session_cache_capacity), - tx, - ), - expired_entries: rx, - } + /// Determines if an ENR is reachable or not based on its assigned keys. + pub fn is_enr_reachable(enr: &Enr) -> bool { + enr.udp4_socket().is_some() || enr.udp6_socket().is_some() } } diff --git a/src/handler/sessions/session.rs b/src/handler/session.rs similarity index 75% rename from src/handler/sessions/session.rs rename to src/handler/session.rs index 209d551b2..4c232b831 100644 --- a/src/handler/sessions/session.rs +++ b/src/handler/session.rs @@ -10,17 +10,12 @@ use crate::{ Discv5Error, Enr, }; -// If the message nonce length is ever set below 4 bytes this will explode. The packet -// size constants shouldn't be modified. -const _: () = assert!(MESSAGE_NONCE_LENGTH > 4); - use enr::{CombinedKey, NodeId}; use parking_lot::RwLock; use std::sync::Arc; -use tracing::warn; use zeroize::Zeroize; -#[derive(Zeroize, PartialEq, Clone, Copy)] +#[derive(Zeroize, PartialEq)] pub(crate) struct Keys { /// The encryption key. encryption_key: [u8; 16], @@ -39,7 +34,6 @@ impl From<([u8; 16], [u8; 16])> for Keys { /// A Session containing the encryption/decryption keys. These are kept individually for a given /// node. -#[derive(Clone)] pub(crate) struct Session { /// The current keys used to encrypt/decrypt messages. keys: Keys, @@ -174,60 +168,23 @@ impl Session { pub(crate) fn establish_from_challenge( local_key: Arc>, local_id: &NodeId, - challenge: Challenge, + remote_id: &NodeId, + challenge_data: ChallengeData, id_nonce_sig: &[u8], ephem_pubkey: &[u8], - enr_record: Option, - node_address: &NodeAddress, - session_limiter: impl FnOnce(&NodeAddress, &Enr) -> Option>, + session_enr: Enr, ) -> Result<(Session, Enr), Discv5Error> { - // check and verify a potential ENR update - - let Challenge { data, remote_enr } = challenge; - - // Avoid cloning an ENR - let session_enr = match (enr_record, remote_enr) { - (Some(new_enr), Some(known_enr)) => { - if new_enr.seq() > known_enr.seq() { - MostRecentEnr::Handshake { - enr: new_enr, - challenge_enr: Some(known_enr), - } - } else { - MostRecentEnr::Challenge { enr: known_enr } - } - } - (Some(new_enr), None) => MostRecentEnr::Handshake { - enr: new_enr, - challenge_enr: None, - }, - (None, Some(known_enr)) => MostRecentEnr::Challenge { enr: known_enr }, - (None, None) => { - warn!( - "Peer did not respond with their ENR. Session could not be established. Node: {}", - node_address.node_id - ); - return Err(Discv5Error::SessionNotEstablished); - } - }; - - // Avoid unnecessary key derivation computation, first verify session candidate against - // current sessions state. - session_limiter(node_address, session_enr.enr()).transpose()?; - - let remote_public_key = session_enr.enr().public_key(); - // verify the auth header nonce if !crypto::verify_authentication_nonce( - &remote_public_key, + &session_enr.public_key(), ephem_pubkey, - &data, + &challenge_data, local_id, id_nonce_sig, ) { let challenge = Challenge { - data, - remote_enr: session_enr.into_challenge_enr(), + data: challenge_data, + remote_enr: Some(session_enr), }; return Err(Discv5Error::InvalidChallengeSignature(challenge)); } @@ -239,8 +196,8 @@ impl Session { let (decryption_key, encryption_key) = crypto::derive_keys_from_pubkey( &local_key.read(), local_id, - &node_address.node_id, - &data, + &remote_id, + &challenge_data, ephem_pubkey, )?; @@ -249,7 +206,6 @@ impl Session { decryption_key, }; - let session_enr = session_enr.into_most_recent_enr(); Ok((Session::new(keys), session_enr)) } @@ -307,41 +263,6 @@ impl Session { } } -enum MostRecentEnr { - Challenge { - enr: Enr, - }, - Handshake { - enr: Enr, - challenge_enr: Option, - }, -} - -impl MostRecentEnr { - fn enr(&self) -> &Enr { - match self { - Self::Challenge { enr } => enr, - Self::Handshake { enr, .. } => enr, - } - } - - fn into_most_recent_enr(self) -> Enr { - match self { - MostRecentEnr::Challenge { enr } => enr, - MostRecentEnr::Handshake { enr, .. } => enr, - } - } - fn into_challenge_enr(self) -> Option { - match self { - MostRecentEnr::Challenge { enr } => Some(enr), - MostRecentEnr::Handshake { - enr: _, - challenge_enr, - } => challenge_enr, - } - } -} - #[cfg(test)] pub(crate) fn build_dummy_session() -> Session { Session::new(Keys { diff --git a/src/handler/sessions/limiter.rs b/src/handler/sessions/limiter.rs deleted file mode 100644 index 70db681b0..000000000 --- a/src/handler/sessions/limiter.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::{node_info::NodeAddress, Discv5Error, Enr}; -use std::collections::HashSet; - -/// The minimum number of peers to accept sessions with that have an unreachable ENR, i.e. cater -/// requests for, at a time. Benevolent peers of this type could for example be symmetrically -/// NAT:ed nodes or nodes that have recently joined the network and are still unaware of their -/// externally reachable socket, relying on their peers to discover it. -pub const MIN_SESSIONS_UNREACHABLE_ENR: usize = 1; - -pub(crate) struct SessionLimiter { - /// Keeps track of the sessions held for peers with unreachable ENRs. These could be peers yet - /// to discover their externally reachable socket or symmetrically NAT:ed peers that, - /// naturally, will never discover one externally reachable socket. - sessions_unreachable_enr_tracker: HashSet, - /// Receiver of expired sessions. - rx_expired_sessions: futures::channel::mpsc::Receiver, - /// The max number of sessions to peers with unreachable ENRs at a time. - limit: usize, -} - -impl SessionLimiter { - pub fn new( - rx_expired_sessions: futures::channel::mpsc::Receiver, - limit: usize, - ) -> Self { - SessionLimiter { - sessions_unreachable_enr_tracker: Default::default(), - rx_expired_sessions, - limit, - } - } - - /// Drains buffer of expired sessions, and untracks any which belong to unreachable ENRs. - fn drain_expired_sessions_buffer(&mut self) { - while let Ok(Some(session_node_address)) = self.rx_expired_sessions.try_next() { - self.sessions_unreachable_enr_tracker - .remove(&session_node_address); - } - } - - /// Checks if a session with this peer should be allowed at this given time. Called after - /// connection establishment, before session key derivation. As a side effect this drains the - /// expired entries buffer. - pub fn track_sessions_unreachable_enr( - &mut self, - node_address: &NodeAddress, - enr: &Enr, - ) -> Result<(), Discv5Error> { - self.drain_expired_sessions_buffer(); - - // Peer is reachable - if enr.udp4_socket().is_some() || enr.udp6_socket().is_some() { - return Ok(()); - } - // Peer is unreachable. - if self.sessions_unreachable_enr_tracker.len() >= self.limit { - return Err(Discv5Error::LimitSessionsUnreachableEnr); - } - - self.sessions_unreachable_enr_tracker - .insert(node_address.clone()); - Ok(()) - } - - /// Untracks the given session if it has an unreachable ENR. - pub fn untrack_session(&mut self, node_address: &NodeAddress) { - self.sessions_unreachable_enr_tracker.remove(node_address); - } -} diff --git a/src/handler/sessions/mod.rs b/src/handler/sessions/mod.rs deleted file mode 100644 index be4aa2c1c..000000000 --- a/src/handler/sessions/mod.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::{lru_time_cache::LruTimeCache, node_info::NodeAddress}; -use std::time::Duration; - -mod crypto; -mod limiter; -pub(crate) mod session; - -use limiter::SessionLimiter; -pub use limiter::MIN_SESSIONS_UNREACHABLE_ENR; -pub(crate) use session::Session; - -pub struct Sessions { - /// Stores established sessions. - pub cache: LruTimeCache, - /// Limits the number of sessions to peers with unreachable ENRs. Limiter is queried upon - /// session establishment. - pub limiter: Option, -} - -impl Sessions { - pub fn new( - cache_capacity: usize, - entry_ttl: Duration, - unreachable_enr_limit: Option, - ) -> Self { - let (cache, limiter) = match unreachable_enr_limit { - Some(limit) => { - let (tx, rx) = futures::channel::mpsc::channel::(cache_capacity); - let limiter = SessionLimiter::new(rx, limit); - let cache = - LruTimeCache::new_with_expiry_feedback(entry_ttl, Some(cache_capacity), tx); - (cache, Some(limiter)) - } - None => { - let cache = LruTimeCache::new(entry_ttl, Some(cache_capacity)); - (cache, None) - } - }; - - Sessions { cache, limiter } - } -} - -#[cfg(test)] -mod test { - use super::*; - use enr::{CombinedKey, EnrBuilder}; - - #[tokio::test] - async fn test_limiter() { - let max_nodes_unreachable_enr = 2; - let session_time_out = Duration::from_secs(1); - let mut sessions = Sessions::new(3, session_time_out, Some(max_nodes_unreachable_enr)); - - // first node - let first_key = CombinedKey::generate_secp256k1(); - let mut builder = EnrBuilder::new("v4"); - let first_enr = builder.build(&first_key).unwrap(); - - let first_unreachable_node = NodeAddress { - socket_addr: "0.0.0.0:10010".parse().unwrap(), - node_id: first_enr.node_id(), - }; - // second node - let second_key = CombinedKey::generate_secp256k1(); - builder = EnrBuilder::new("v4"); - let second_enr = builder.build(&second_key).unwrap(); - - let second_unreachable_node = NodeAddress { - socket_addr: "0.0.0.0:10011".parse().unwrap(), - node_id: second_enr.node_id(), - }; - // third node - let third_key = CombinedKey::generate_secp256k1(); - builder = EnrBuilder::new("v4"); - let third_enr = builder.build(&third_key).unwrap(); - - let third_unreachable_node = NodeAddress { - socket_addr: "0.0.0.0:10012".parse().unwrap(), - node_id: third_enr.node_id(), - }; - // check if space for first node - let res = sessions.limiter.as_mut().map(|limiter| { - limiter.track_sessions_unreachable_enr(&first_unreachable_node, &first_enr) - }); - res.unwrap() - .expect("should be space for first unreachable node"); - // insert first node - let first_session = Session::new(([0u8; 16], [0u8; 16]).into()); - sessions.cache.insert(first_unreachable_node, first_session); - // check if space for second node - let second_res = sessions.limiter.as_mut().map(|limiter| { - limiter.track_sessions_unreachable_enr(&second_unreachable_node, &second_enr) - }); - second_res - .unwrap() - .expect("should be space for second unreachable node"); - // insert second node - let second_session = Session::new(([0u8; 16], [0u8; 16]).into()); - sessions - .cache - .insert(second_unreachable_node, second_session); - - // check if space for third node, should fail - let third_res = sessions.limiter.as_mut().map(|limiter| { - limiter.track_sessions_unreachable_enr(&third_unreachable_node, &third_enr) - }); - assert!(third_res.unwrap().is_err()); - // let sessions expire - tokio::time::sleep(session_time_out).await; - // calling `len` removes expired entries - sessions.cache.len(); - // retry check if space for third node, should be successful - let third_res = sessions.limiter.as_mut().map(|limiter| { - limiter.track_sessions_unreachable_enr(&third_unreachable_node, &third_enr) - }); - third_res - .unwrap() - .expect("should be space for third unreachable node"); - } -} diff --git a/src/handler/tests.rs b/src/handler/tests.rs index 15b1946ed..b2cdc8046 100644 --- a/src/handler/tests.rs +++ b/src/handler/tests.rs @@ -2,7 +2,7 @@ use super::*; use crate::{ - handler::sessions::session::build_dummy_session, + handler::session::build_dummy_session, packet::{DefaultProtocolId, PacketHeader, MAX_PACKET_SIZE}, return_if_ipv6_is_not_supported, rpc::{Request, Response}, @@ -75,13 +75,14 @@ async fn build_handler_with_listen_config( let (service_send, handler_recv) = mpsc::channel(50); let (exit_tx, exit) = oneshot::channel(); - let nat_hole_puncher = NatHolePunchUtils::new( + let nat_utils = NatUtils::new( listen_sockets.iter(), &enr, config.listen_config.ip_mode(), None, None, config.session_cache_capacity, + None, ); ( @@ -93,7 +94,7 @@ async fn build_handler_with_listen_config( active_requests: ActiveRequests::new(config.request_timeout), pending_requests: HashMap::new(), filter_expected_responses, - sessions: Sessions::new(config.session_cache_capacity, config.session_timeout, None), + sessions: LruTimeCache::new(config.session_timeout,Some(config.session_cache_capacity)), one_time_sessions: LruTimeCache::new( Duration::from_secs(ONE_TIME_SESSION_TIMEOUT), Some(ONE_TIME_SESSION_CACHE_CAPACITY), @@ -103,7 +104,7 @@ async fn build_handler_with_listen_config( service_send, listen_sockets, socket, - nat_hole_puncher, + nat_utils, exit, }, MockService { @@ -494,7 +495,6 @@ async fn relay() { build_handler_with_listen_config::(listen_config).await; let relay_addr = handler.enr.read().udp4_socket().unwrap().into(); let relay_node_id = handler.enr.read().node_id(); - let mut dummy_session = build_dummy_session(); // Initiator let initr_enr = { @@ -511,8 +511,7 @@ async fn relay() { let initr_node_address = NodeAddress::new(initr_addr, initr_enr.node_id()); handler .sessions - .cache - .insert(initr_node_address, dummy_session.clone()); + .insert(initr_node_address, build_dummy_session()); let initr_socket = UdpSocket::bind(initr_addr) .await @@ -533,8 +532,7 @@ async fn relay() { let tgt_node_address = NodeAddress::new(tgt_addr, tgt_enr.node_id()); handler .sessions - .cache - .insert(tgt_node_address, dummy_session.clone()); + .insert(tgt_node_address, build_dummy_session()); let tgt_socket = UdpSocket::bind(tgt_addr) .await @@ -618,7 +616,7 @@ async fn relay() { kind ); - let decrypted_message = dummy_session + let decrypted_message = build_dummy_session() .decrypt_message(message_nonce, &message, &aad) .expect("should decrypt message"); match Message::decode(&decrypted_message).expect("should decode message") { diff --git a/src/lru_time_cache.rs b/src/lru_time_cache.rs index 79e8a9756..380426e15 100644 --- a/src/lru_time_cache.rs +++ b/src/lru_time_cache.rs @@ -1,19 +1,20 @@ -use futures::channel::mpsc; use hashlink::LinkedHashMap; use std::{ hash::Hash, time::{Duration, Instant}, }; -use tracing::warn; pub struct LruTimeCache { - map: LinkedHashMap, + /// The main map storing the internal values. It stores the time the value was inserted and an + /// optional tag to keep track of individual values. + map: LinkedHashMap, /// The time elements remain in the cache. ttl: Duration, /// The max size of the cache. capacity: usize, - /// Channel to send expiry notifications on. - tx: Option>, + /// Optional count of specific tagged elements. This is used in discv5 for tracking + /// the number of unreachable sessions currently held. + tagged_count: usize, } impl LruTimeCache { @@ -27,36 +28,49 @@ impl LruTimeCache { map: LinkedHashMap::new(), ttl, capacity, - tx: None, + tagged_count: 0, } } - /// Queues expired entries on buffer. If enabled, ensure that buffer is emptied regularly. - pub fn new_with_expiry_feedback( - ttl: Duration, - capacity: Option, - tx: mpsc::Sender, - ) -> LruTimeCache { - let capacity = if let Some(cap) = capacity { - cap - } else { - usize::MAX - }; - LruTimeCache { - map: LinkedHashMap::new(), - ttl, - capacity, - tx: Some(tx), - } + /// Returns the number of elements that are currently tagged in the cache. + pub fn tagged(&self) -> usize { + self.tagged_count } - /// Inserts a key-value pair into the cache. + // Insert an untagged key-value pair into the cache. pub fn insert(&mut self, key: K, value: V) { + self.insert_raw(key, value, false); + } + + // Insert a tagged key-value pair into the cache. + #[allow(dead_code)] + pub fn insert_tagged(&mut self, key: K, value: V) { + self.insert_raw(key, value, true); + } + + /// Inserts a key-value pair into the cache. + pub fn insert_raw(&mut self, key: K, value: V, tagged: bool) { let now = Instant::now(); - self.map.insert(key, (value, now)); + if let Some(old_value) = self.map.insert(key, (value, now, tagged)) { + // If the old value was tagged but the new one isn't, we reduce our count + if !tagged && old_value.2 { + self.tagged_count = self.tagged_count.saturating_sub(1); + } else if tagged && !old_value.2 { + // Else if the new value is tagged and the old wasn't tagged increment the count + self.tagged_count += 1; + } + } else if tagged { + // No previous value, increment the tagged count + self.tagged_count += 1; + } if self.map.len() > self.capacity { - self.map.pop_front(); + if let Some((_, value)) = self.map.pop_front() { + if value.2 { + // We have removed a tagged element + self.tagged_count = self.tagged_count.saturating_sub(1); + } + } } } @@ -87,14 +101,13 @@ impl LruTimeCache { /// updating the timestamp. #[allow(dead_code)] pub fn peek(&self, key: &K) -> Option<&V> { - if let Some((value, time)) = self.map.get(key) { + if let Some((value, time, _)) = self.map.get(key) { return if *time + self.ttl >= Instant::now() { Some(value) } else { None }; } - None } @@ -107,28 +120,31 @@ impl LruTimeCache { /// Removes a key-value pair from the cache, returning the value at the key if the key /// was previously in the map. pub fn remove(&mut self, key: &K) -> Option { - self.map.remove(key).map(|v| v.0) + let value = self.map.remove(key)?; + + // This element was tagged, reduce the count + if value.2 { + self.tagged_count = self.tagged_count.saturating_sub(1); + } + Some(value.0) } /// Removes expired items from the cache. fn remove_expired_values(&mut self, now: Instant) { let mut expired_keys = vec![]; - for (key, (_, time)) in self.map.iter_mut() { + for (key, (_, time, _)) in self.map.iter_mut() { if *time + self.ttl >= now { break; } expired_keys.push(key.clone()); } - for k in expired_keys.iter() { - self.map.remove(k); - } - - if let Some(ref mut tx) = self.tx { - for k in expired_keys { - if let Err(e) = tx.try_send(k) { - warn!("Failed removing expired key, {}", e); + for k in expired_keys { + if let Some(v) = self.map.remove(&k) { + // This key was tagged, reduce the count + if v.2 { + self.tagged_count = self.tagged_count.saturating_sub(1); } } } @@ -171,6 +187,30 @@ mod tests { assert_eq!(Some(&30), cache.get(&3)); } + #[test] + fn tagging() { + let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2)); + + cache.insert_tagged(1, 10); + cache.insert(2, 20); + assert_eq!(2, cache.len()); + assert_eq!(1, cache.tagged()); + + cache.insert_tagged(3, 30); + assert_eq!(2, cache.len()); + assert_eq!(1, cache.tagged()); + assert_eq!(Some(&20), cache.get(&2)); + assert_eq!(Some(&30), cache.get(&3)); + + cache.insert_tagged(2, 30); + assert_eq!(2, cache.tagged()); + + cache.insert(4, 30); + assert_eq!(1, cache.tagged()); + cache.insert(5, 30); + assert_eq!(0, cache.tagged()); + } + #[test] fn get() { let mut cache = LruTimeCache::new(Duration::from_secs(10), Some(2)); diff --git a/src/packet/mod.rs b/src/packet/mod.rs index 3207ea4b3..c59e72e08 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -24,7 +24,7 @@ pub const IV_LENGTH: usize = 16; /// The length of the static header. (6 byte protocol id, 2 bytes version, 1 byte kind, 12 byte /// message nonce and a 2 byte authdata-size). pub const STATIC_HEADER_LENGTH: usize = 23; -/// The message nonce length (in bytes). +/// The message nonce length (in bytes). This must be at least 4 bytes. pub const MESSAGE_NONCE_LENGTH: usize = 12; /// The Id nonce length (in bytes). pub const ID_NONCE_LENGTH: usize = 16; From 402c964fe9000e774b89b02af4df1ff966b0e32e Mon Sep 17 00:00:00 2001 From: Age Manning Date: Wed, 17 Jan 2024 15:12:00 +1100 Subject: [PATCH 146/154] Add reviewers comments --- src/handler/mod.rs | 8 ++++---- src/handler/nat_hole_punch/utils.rs | 13 ++++++++----- src/handler/session.rs | 2 +- src/handler/tests.rs | 2 +- src/lru_time_cache.rs | 4 +--- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 60820eaf9..0d108ea25 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -320,7 +320,7 @@ impl Handler { let sessions = LruTimeCache::new(session_timeout, Some(session_cache_capacity)); let nat_utils = NatUtils::new( - listen_sockets.iter(), + &listen_sockets, &enr.read(), ip_mode, unused_port_range, @@ -408,7 +408,7 @@ impl Handler { warn!("Failed to inform that request failed {}", e); } } - self.nat_utils.set_is_behind_nat(self.listen_sockets.iter(), Some(ip), Some(port)); + self.nat_utils.set_is_behind_nat(&self.listen_sockets, Some(ip), Some(port)); } } } @@ -930,9 +930,9 @@ impl Handler { // Peer is reachable let enr_not_reachable = !NatUtils::is_enr_reachable(&most_recent_enr); - // Decide whether to establish this connection based on our apettiite for unreachable + // Decide whether to establish this connection based on our appetite for unreachable if enr_not_reachable - && Some(self.sessions.tagged()) > self.nat_utils.unreachable_enr_limit + && Some(self.sessions.tagged()) >= self.nat_utils.unreachable_enr_limit { debug!("Reached limit of unreachable ENR sessions. Avoiding a new connection. Limit: {}", self.sessions.tagged()); return; diff --git a/src/handler/nat_hole_punch/utils.rs b/src/handler/nat_hole_punch/utils.rs index d3ed4d724..5674fb238 100644 --- a/src/handler/nat_hole_punch/utils.rs +++ b/src/handler/nat_hole_punch/utils.rs @@ -43,8 +43,8 @@ pub struct NatUtils { } impl NatUtils { - pub fn new<'a>( - listen_sockets: impl Iterator, + pub fn new( + listen_sockets: &[SocketAddr], local_enr: &Enr, ip_mode: IpMode, unused_port_range: Option>, @@ -96,13 +96,16 @@ impl NatUtils { /// Called when a new observed address is reported at start up or after a /// [`crate::Discv5Event::SocketUpdated`]. - pub fn set_is_behind_nat<'a>( + pub fn set_is_behind_nat( &mut self, - mut listen_sockets: impl Iterator, + listen_sockets: &[SocketAddr], observed_ip: Option, observed_port: Option, ) { - if !listen_sockets.any(|listen_socket| Some(listen_socket.port()) == observed_port) { + if !listen_sockets + .iter() + .any(|listen_socket| Some(listen_socket.port()) == observed_port) + { self.is_behind_nat = Some(true); return; } diff --git a/src/handler/session.rs b/src/handler/session.rs index 4c232b831..d0f53f305 100644 --- a/src/handler/session.rs +++ b/src/handler/session.rs @@ -196,7 +196,7 @@ impl Session { let (decryption_key, encryption_key) = crypto::derive_keys_from_pubkey( &local_key.read(), local_id, - &remote_id, + remote_id, &challenge_data, ephem_pubkey, )?; diff --git a/src/handler/tests.rs b/src/handler/tests.rs index 6156d3f8e..b82099010 100644 --- a/src/handler/tests.rs +++ b/src/handler/tests.rs @@ -76,7 +76,7 @@ async fn build_handler_with_listen_config( let (exit_tx, exit) = oneshot::channel(); let nat_utils = NatUtils::new( - listen_sockets.iter(), + &listen_sockets, &enr, config.listen_config.ip_mode(), None, diff --git a/src/lru_time_cache.rs b/src/lru_time_cache.rs index 380426e15..b90336ab2 100644 --- a/src/lru_time_cache.rs +++ b/src/lru_time_cache.rs @@ -43,7 +43,7 @@ impl LruTimeCache { } // Insert a tagged key-value pair into the cache. - #[allow(dead_code)] + #[cfg(test)] pub fn insert_tagged(&mut self, key: K, value: V) { self.insert_raw(key, value, true); } @@ -76,7 +76,6 @@ impl LruTimeCache { /// Retrieves a reference to the value stored under `key`, or `None` if the key doesn't exist. /// Also removes expired elements and updates the time. - #[allow(dead_code)] pub fn get(&mut self, key: &K) -> Option<&V> { self.get_mut(key).map(|value| &*value) } @@ -99,7 +98,6 @@ impl LruTimeCache { /// Returns a reference to the value with the given `key`, if present and not expired, without /// updating the timestamp. - #[allow(dead_code)] pub fn peek(&self, key: &K) -> Option<&V> { if let Some((value, time, _)) = self.map.get(key) { return if *time + self.ttl >= Instant::now() { From e50661c1a4073ca5a8f96a3d8d1fda656fb38178 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Wed, 17 Jan 2024 16:32:44 +1100 Subject: [PATCH 147/154] Simplify the code, name changes group handler API --- Cargo.toml | 2 - src/error.rs | 11 ++ src/handler/mod.rs | 147 ++++++++++-------- .../{nat_hole_punch/utils.rs => nat.rs} | 14 +- src/handler/nat_hole_punch/error.rs | 14 -- src/handler/nat_hole_punch/mod.rs | 50 ------ src/handler/tests.rs | 18 +-- src/service.rs | 63 ++++---- 8 files changed, 138 insertions(+), 181 deletions(-) rename src/handler/{nat_hole_punch/utils.rs => nat.rs} (94%) delete mode 100644 src/handler/nat_hole_punch/error.rs delete mode 100644 src/handler/nat_hole_punch/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 4d911c3b4..351c7d62d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,9 +38,7 @@ lru = {version = "0.7.1", default-features = false } hashlink = "0.7.0" delay_map = "0.3.0" more-asserts = "0.2.2" -thiserror = "1.0.40" derive_more = { version = "0.99.17", default-features = false, features = ["from", "display", "deref", "deref_mut"] } -async-trait = "0.1.74" [dev-dependencies] rand_07 = { package = "rand", version = "0.7" } diff --git a/src/error.rs b/src/error.rs index c6323dffb..3afd77502 100644 --- a/src/error.rs +++ b/src/error.rs @@ -51,6 +51,17 @@ pub enum Discv5Error { Io(std::io::Error), } +/// An error occurred whilst attempting to hole punch NAT. +#[derive(Debug)] +pub enum NatError { + /// Initiator error. + Initiator(Discv5Error), + /// Relayer error. + Relay(Discv5Error), + /// Target error. + Target(Discv5Error), +} + macro_rules! impl_from_variant { ($(<$($generic: ident,)+>)*, $from_type: ty, $to_type: ty, $variant: path) => { impl$(<$($generic,)+>)* From<$from_type> for $to_type { diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 0d108ea25..3618ebf06 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -29,7 +29,7 @@ use crate::{ config::Discv5Config, discv5::PERMIT_BAN_LIST, - error::{Discv5Error, RequestError}, + error::{Discv5Error, RequestError, NatError}, packet::{ChallengeData, IdNonce, MessageNonce, Packet, PacketKind, ProtocolIdentity}, rpc::{ Message, Payload, RelayInitNotification, RelayMsgNotification, Request, RequestBody, @@ -59,7 +59,7 @@ use tracing::{debug, error, trace, warn}; mod active_requests; mod crypto; -mod nat_hole_punch; +mod nat; mod request_call; mod session; mod tests; @@ -69,7 +69,7 @@ pub use crate::node_info::{NodeAddress, NodeContact}; use crate::{lru_time_cache::LruTimeCache, socket::ListenConfig}; use active_requests::ActiveRequests; -use nat_hole_punch::{Error as NatError, HolePunchNat, NatUtils}; +use nat::Nat; use request_call::RequestCall; use session::Session; @@ -85,7 +85,6 @@ const ONE_TIME_SESSION_CACHE_CAPACITY: usize = 100; /// Messages sent from the application layer to `Handler`. #[derive(Debug, Clone, PartialEq, Eq)] -#[allow(clippy::large_enum_variant)] pub enum HandlerIn { /// A Request to send to a `NodeContact` has been received from the application layer. A /// `NodeContact` is an abstract type that allows for either an ENR to be sent or a `Raw` type @@ -108,15 +107,9 @@ pub enum HandlerIn { /// response back to the `NodeAddress` from which the request was received. Response(NodeAddress, Box), - /// A Random packet has been received and we have requested the application layer to inform - /// us what the highest known ENR is for this node. - /// The `WhoAreYouRef` is sent out in the `HandlerOut::WhoAreYou` event and should - /// be returned here to submit the application's response. - WhoAreYou(WhoAreYouRef, Option), - - /// A response to a [`HandlerOut::FindHolePunchEnr`]. Returns the ENR and the - /// [`RelayInitNotification`] from [`HandlerOut::FindHolePunchEnr`]. - HolePunchEnr(Enr, RelayInitNotification), + /// The application layer is responding with an ENR to a `RequestEnr` request. This function + /// returns the requested data and optionally and ENR if one is found. + EnrResponse(Option, EnrRequestData), /// Observed socket has been update. The old socket and the current socket. SocketUpdate(Option, SocketAddr), @@ -138,19 +131,15 @@ pub enum HandlerOut { /// A Response has been received from a node on the network. Response(NodeAddress, Box), - /// An unknown source has requested information from us. Return the reference with the known - /// ENR of this node (if known). See the `HandlerIn::WhoAreYou` variant. - WhoAreYou(WhoAreYouRef), + /// We need to request the ENR of a specific node. This could be due to an unknown ENR or a + /// hole punch request. + RequestEnr(EnrRequestData), /// An RPC request failed. /// /// This returns the request ID and an error indicating why the request failed. RequestFailed(RequestId, RequestError), - /// Look-up an ENR in k-buckets. Passes the node id of the peer to look up and the - /// [`RelayMsgNotification`] we intend to send to it. - FindHolePunchEnr(RelayInitNotification), - /// Triggers a ping to all peers, outside of the regular ping interval. Needed to trigger /// renewed session establishment after updating the local ENR from unreachable to reachable /// and clearing all sessions. Only this way does the local node have a chance to make it into @@ -170,6 +159,19 @@ pub enum ConnectionDirection { Outgoing, } +/// The kind of request data being sent to the service. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EnrRequestData { + /// A Random packet has been received and request the application layer to inform + /// us what the highest known ENR is for this node. + /// The `WhoAreYouRef` is sent out in the `HandlerOut::WhoAreYou` event and should + /// be returned here to submit the application's response. + WhoAreYou(WhoAreYouRef), + /// Look-up an ENR in k-buckets. Passes the node id of the peer to look up and the + /// [`RelayMsgNotification`] we intend to send to it. + Nat(RelayInitNotification), +} + /// A reference for the application layer to send back when the handler requests any known /// ENR for the NodeContact. #[derive(Debug, Clone, PartialEq, Eq)] @@ -233,8 +235,8 @@ pub struct Handler { socket: Socket, /// Exit channel to shutdown the handler. exit: oneshot::Receiver<()>, - /// Types necessary to plug in nat hole punching. - nat_utils: NatUtils, + /// Struct to handle nat hole punching logic. + nat: Nat, } type HandlerReturn = ( @@ -319,7 +321,7 @@ impl Handler { let sessions = LruTimeCache::new(session_timeout, Some(session_cache_capacity)); - let nat_utils = NatUtils::new( + let nat = Nat::new( &listen_sockets, &enr.read(), ip_mode, @@ -350,7 +352,7 @@ impl Handler { service_send, listen_sockets, socket, - nat_utils, + nat, exit, }; debug!("Handler Starting"); @@ -378,15 +380,19 @@ impl Handler { } } HandlerIn::Response(dst, response) => self.send_response::

(dst, *response).await, - HandlerIn::WhoAreYou(wru_ref, enr) => self.send_challenge::

(wru_ref, enr).await, - HandlerIn::HolePunchEnr(tgt_enr, relay_init) => { + HandlerIn::EnrResponse(enr, EnrRequestData::WhoAreYou(wru_ref)) => self.send_challenge::

(wru_ref, enr).await, + HandlerIn::EnrResponse(Some(target_enr), EnrRequestData::Nat(relay_initiator)) => { // Assemble the notification for the target - let (inr_enr, _tgt, timed_out_nonce) = relay_init.into(); - let relay_msg_notif = RelayMsgNotification::new(inr_enr, timed_out_nonce); - if let Err(e) = self.send_relay_msg_notif::

(tgt_enr, relay_msg_notif).await { - warn!("Failed to relay. Error: {}", e); + let (initiator_enr, _target, timed_out_nonce) = relay_initiator.into(); + let relay_msg_notification = RelayMsgNotification::new(initiator_enr, timed_out_nonce); + if let Err(e) = self.send_relay_msg_notification::

(target_enr, relay_msg_notification).await { + warn!("Failed to relay. Error: {:?}", e); } } + HandlerIn::EnrResponse(_,_) => {} // This handles the case that No ENR was + // found for a target relayer. This + // message never gets sent, so it is + // ignored. HandlerIn::SocketUpdate(old_socket, socket) => { let ip = socket.ip(); let port = socket.port(); @@ -408,7 +414,7 @@ impl Handler { warn!("Failed to inform that request failed {}", e); } } - self.nat_utils.set_is_behind_nat(&self.listen_sockets, Some(ip), Some(port)); + self.nat.set_is_behind_nat(&self.listen_sockets, Some(ip), Some(port)); } } } @@ -423,14 +429,14 @@ impl Handler { // challenge. We process them here self.send_next_request::

(node_address).await; } - Some(Ok(peer_socket)) = self.nat_utils.hole_punch_tracker.next() => { - if self.nat_utils.is_behind_nat == Some(false) { + Some(Ok(peer_socket)) = self.nat.hole_punch_tracker.next() => { + if self.nat.is_behind_nat == Some(false) { // Until ip voting is done and an observed public address is finalised, all nodes act as // if they are behind a NAT. return; } if let Err(e) = self.on_hole_punch_expired(peer_socket).await { - warn!("Failed to keep hole punched for peer, error: {}", e); + warn!("Failed to keep hole punched for peer, error: {:?}", e); } } _ = banned_nodes_check.tick() => self.unban_nodes_check(), // Unban nodes that are past the timeout @@ -539,7 +545,7 @@ impl Handler { if request_call.retries() >= self.request_retries { trace!("Request timed out with {}", node_address); if let Some(relay) = self - .nat_utils + .nat .new_peer_latest_relay_cache .pop(&node_address.node_id) { @@ -836,7 +842,7 @@ impl Handler { ConnectionDirection::Incoming }; - enr_not_reachable = NatUtils::is_enr_reachable(&enr); + enr_not_reachable = Nat::is_enr_reachable(&enr); // We already know the ENR. Send the handshake response packet trace!("Sending Authentication response to node: {}", node_address); @@ -928,11 +934,11 @@ impl Handler { // Keep count of the unreachable Sessions we are tracking // Peer is reachable - let enr_not_reachable = !NatUtils::is_enr_reachable(&most_recent_enr); + let enr_not_reachable = !Nat::is_enr_reachable(&most_recent_enr); // Decide whether to establish this connection based on our appetite for unreachable if enr_not_reachable - && Some(self.sessions.tagged()) >= self.nat_utils.unreachable_enr_limit + && Some(self.sessions.tagged()) >= self.nat.unreachable_enr_limit { debug!("Reached limit of unreachable ENR sessions. Avoiding a new connection. Limit: {}", self.sessions.tagged()); return; @@ -962,7 +968,7 @@ impl Handler { ) .await; self.new_session(node_address.clone(), session, enr_not_reachable); - self.nat_utils + self.nat .new_peer_latest_relay_cache .pop(&node_address.node_id); self.handle_message::

( @@ -1137,14 +1143,14 @@ impl Handler { warn!("peer {node_address} tried to initiate hole punch attempt for another node {initiator_node_id}, banning peer {node_address}"); self.fail_session(&node_address, RequestError::MaliciousRelayInit, true) .await; - let ban_timeout = self.nat_utils.ban_duration.map(|v| Instant::now() + v); + let ban_timeout = self.nat.ban_duration.map(|v| Instant::now() + v); PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); - } else if let Err(e) = self.on_relay_init(notification).await { - warn!("failed handling notification to relay for {node_address}, {e}"); + } else if let Err(e) = self.on_relay_initiator(notification).await { + warn!("failed handling notification to relay for {node_address}, {:?}", e); } } Message::RelayMsgNotification(notification) => { - match self.nat_utils.is_behind_nat { + match self.nat.is_behind_nat { Some(false) => { // inr may not be malicious and initiated a hole punch attempt when // a request to this node timed out for another reason @@ -1152,7 +1158,7 @@ impl Handler { } _ => { if let Err(e) = self.on_relay_msg::

(notification).await { - warn!("failed handling notification relayed from {node_address}, {e}"); + warn!("failed handling notification relayed from {node_address}, {:?}", e); } } } @@ -1205,7 +1211,7 @@ impl Handler { let whoareyou_ref = WhoAreYouRef(node_address, message_nonce); if let Err(e) = self .service_send - .send(HandlerOut::WhoAreYou(whoareyou_ref)) + .send(HandlerOut::RequestEnr(EnrRequestData::WhoAreYou(whoareyou_ref))) .await { warn!("Failed to send WhoAreYou to the service {}", e) @@ -1251,7 +1257,7 @@ impl Handler { let whoareyou_ref = WhoAreYouRef(node_address, message_nonce); if let Err(e) = self .service_send - .send(HandlerOut::WhoAreYou(whoareyou_ref)) + .send(HandlerOut::RequestEnr(EnrRequestData::WhoAreYou(whoareyou_ref))) .await { warn!( @@ -1333,14 +1339,14 @@ impl Handler { // extra responses if let ResponseBody::Nodes { total, ref nodes } = response.body { for node in nodes { - if let Some(socket_addr) = self.nat_utils.ip_mode.get_contactable_addr(node) { + if let Some(socket_addr) = self.nat.ip_mode.get_contactable_addr(node) { let node_id = node.node_id(); let new_peer_node_address = NodeAddress { socket_addr, node_id, }; if self.sessions.peek(&new_peer_node_address).is_none() { - self.nat_utils + self.nat .new_peer_latest_relay_cache .put(node_id, node_address.clone()); } @@ -1472,7 +1478,7 @@ impl Handler { } } let node_address = request_call.contact().node_address(); - self.nat_utils + self.nat .new_peer_latest_relay_cache .pop(&node_address.node_id); self.fail_session(&node_address, error, remove_session) @@ -1492,7 +1498,7 @@ impl Handler { .active_sessions .store(self.sessions.len(), Ordering::Relaxed); // stop keeping hole punched for peer - self.nat_utils.untrack(&node_address.socket_addr); + self.nat.untrack(&node_address.socket_addr); } if let Some(to_remove) = self.pending_requests.remove(node_address) { for PendingRequest { request_id, .. } in to_remove { @@ -1529,7 +1535,7 @@ impl Handler { if let Err(e) = self.socket.send.send(packet).await { warn!("Failed to send outbound packet {}", e) } - self.nat_utils.track(dst); + self.nat.track(dst); } /// Check if any banned nodes have served their time and unban them. @@ -1580,8 +1586,12 @@ fn most_recent_enr(first: Option, second: Option) -> Result { } } -#[async_trait::async_trait] -impl HolePunchNat for Handler { + +// NAT-related functions +impl Handler { + /// A request times out. Should trigger the initiation of a hole punch attempt, given a + /// transitive route to the target exists. Sends a RELAYINIT notification to the given + /// relay. async fn on_request_time_out( &mut self, relay: NodeAddress, @@ -1624,11 +1634,13 @@ impl HolePunchNat for Handler { Ok(()) } - async fn on_relay_init(&mut self, relay_init: RelayInitNotification) -> Result<(), NatError> { + /// A RelayInit notification is received over discv5 indicating this node is the relay. Should + /// trigger sending a RelayMsg to the target. + async fn on_relay_initiator(&mut self, relay_initiator: RelayInitNotification) -> Result<(), NatError> { // Check for target peer in our kbuckets otherwise drop notification. if let Err(e) = self .service_send - .send(HandlerOut::FindHolePunchEnr(relay_init)) + .send(HandlerOut::RequestEnr(EnrRequestData::Nat(relay_initiator))) .await { return Err(NatError::Relay(e.into())); @@ -1636,13 +1648,15 @@ impl HolePunchNat for Handler { Ok(()) } + /// A RelayMsg notification is received over discv5 indicating this node is the target. Should + /// trigger a WHOAREYOU to be sent to the initiator using the `nonce` in the RelayMsg. async fn on_relay_msg( &mut self, relay_msg: RelayMsgNotification, ) -> Result<(), NatError> { let (inr_enr, timed_out_msg_nonce) = relay_msg.into(); let initiator_node_address = - match NodeContact::try_from_enr(inr_enr, self.nat_utils.ip_mode) { + match NodeContact::try_from_enr(inr_enr, self.nat.ip_mode) { Ok(contact) => contact.node_address(), Err(e) => return Err(NatError::Target(e.into())), }; @@ -1670,31 +1684,32 @@ impl HolePunchNat for Handler { Ok(()) } - async fn send_relay_msg_notif( + /// Send a RELAYMSG notification. + async fn send_relay_msg_notification( &mut self, - tgt_enr: Enr, - relay_msg_notif: RelayMsgNotification, + target_enr: Enr, + relay_msg_notification: RelayMsgNotification, ) -> Result<(), NatError> { - let tgt_node_address = match NodeContact::try_from_enr(tgt_enr, self.nat_utils.ip_mode) { + let target_node_address = match NodeContact::try_from_enr(target_enr, self.nat.ip_mode) { Ok(contact) => contact.node_address(), Err(e) => return Err(NatError::Relay(e.into())), }; - if let Some(session) = self.sessions.get_mut(&tgt_node_address) { + if let Some(session) = self.sessions.get_mut(&target_node_address) { trace!( - "Sending notif to target {}. relay msg: {}", - tgt_node_address.node_id, - relay_msg_notif, + "Sending notification to target {}. relay msg: {}", + target_node_address.node_id, + relay_msg_notification, ); // Encrypt the notification and send let packet = match session - .encrypt_session_message::

(self.node_id, &relay_msg_notif.encode()) + .encrypt_session_message::

(self.node_id, &relay_msg_notification.encode()) { Ok(packet) => packet, Err(e) => { return Err(NatError::Relay(e)); } }; - self.send(tgt_node_address, packet).await; + self.send(target_node_address, packet).await; Ok(()) } else { // Either the session is being established or has expired. We simply drop the diff --git a/src/handler/nat_hole_punch/utils.rs b/src/handler/nat.rs similarity index 94% rename from src/handler/nat_hole_punch/utils.rs rename to src/handler/nat.rs index 5674fb238..b9e859f65 100644 --- a/src/handler/nat_hole_punch/utils.rs +++ b/src/handler/nat.rs @@ -19,7 +19,7 @@ pub const PORT_BIND_TRIES: usize = 4; pub const USER_AND_DYNAMIC_PORTS: RangeInclusive = 1025..=u16::MAX; /// Aggregates types necessary to implement nat hole punching for [`crate::handler::Handler`]. -pub struct NatUtils { +pub struct Nat { /// Ip mode as set in config. pub ip_mode: IpMode, /// This node has been observed to be behind a NAT. @@ -42,7 +42,7 @@ pub struct NatUtils { pub unreachable_enr_limit: Option, } -impl NatUtils { +impl Nat { pub fn new( listen_sockets: &[SocketAddr], local_enr: &Enr, @@ -52,7 +52,7 @@ impl NatUtils { session_cache_capacity: usize, unreachable_enr_limit: Option, ) -> Self { - let mut nat_hole_puncher = NatUtils { + let mut nat = Nat { ip_mode, is_behind_nat: None, new_peer_latest_relay_cache: LruCache::new(session_cache_capacity), @@ -70,17 +70,17 @@ impl NatUtils { local_enr.udp6(), ) { (Some(ip), port, _, _) => { - nat_hole_puncher.set_is_behind_nat(listen_sockets, Some(ip.into()), port); + nat.set_is_behind_nat(listen_sockets, Some(ip.into()), port); } (_, _, Some(ip6), port) => { - nat_hole_puncher.set_is_behind_nat(listen_sockets, Some(ip6.into()), port); + nat.set_is_behind_nat(listen_sockets, Some(ip6.into()), port); } (None, Some(port), _, _) | (_, _, None, Some(port)) => { - nat_hole_puncher.set_is_behind_nat(listen_sockets, None, Some(port)); + nat.set_is_behind_nat(listen_sockets, None, Some(port)); } (None, None, None, None) => {} } - nat_hole_puncher + nat } pub fn track(&mut self, peer_socket: SocketAddr) { diff --git a/src/handler/nat_hole_punch/error.rs b/src/handler/nat_hole_punch/error.rs deleted file mode 100644 index 58d48b522..000000000 --- a/src/handler/nat_hole_punch/error.rs +++ /dev/null @@ -1,14 +0,0 @@ -use thiserror::Error; - -use crate::Discv5Error; - -/// An error occurred whilst attempting to hole punch NAT. -#[derive(Debug, Error)] -pub enum Error { - #[error("NAT error, failed as initiator of a hole punch attempt, {0}")] - Initiator(Discv5Error), - #[error("NAT error, failed as relay of a hole punch attempt, {0}")] - Relay(Discv5Error), - #[error("NAT error, failed as target of a hole punch attempt, {0}")] - Target(Discv5Error), -} diff --git a/src/handler/nat_hole_punch/mod.rs b/src/handler/nat_hole_punch/mod.rs deleted file mode 100644 index e5b03a6fb..000000000 --- a/src/handler/nat_hole_punch/mod.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::net::SocketAddr; - -use crate::{ - node_info::NodeAddress, - packet::MessageNonce, - rpc::{RelayInitNotification, RelayMsgNotification}, - Enr, ProtocolIdentity, -}; - -mod error; -mod utils; - -pub use error::Error; -pub use utils::NatUtils; - -#[async_trait::async_trait] -pub trait HolePunchNat { - /// A request times out. Should trigger the initiation of a hole punch attempt, given a - /// transitive route to the target exists. Sends a RELAYINIT notification to the given - /// relay. - async fn on_request_time_out( - &mut self, - relay: NodeAddress, - local_enr: Enr, // initiator-enr - timed_out_nonce: MessageNonce, - target_node_address: NodeAddress, - ) -> Result<(), Error>; - - /// A RelayInit notification is received over discv5 indicating this node is the relay. Should - /// trigger sending a RelayMsg to the target. - async fn on_relay_init(&mut self, relay_init: RelayInitNotification) -> Result<(), Error>; - - /// A RelayMsg notification is received over discv5 indicating this node is the target. Should - /// trigger a WHOAREYOU to be sent to the initiator using the `nonce` in the RelayMsg. - async fn on_relay_msg( - &mut self, - relay_msg: RelayMsgNotification, - ) -> Result<(), Error>; - - /// Send a RELAYMSG notification. - async fn send_relay_msg_notif( - &mut self, - tgt_enr: Enr, - relay_msg_notif: RelayMsgNotification, - ) -> Result<(), Error>; - - /// A hole punched for a peer closes. Should trigger an empty packet to be sent to the - /// peer to keep it open. - async fn on_hole_punch_expired(&mut self, peer: SocketAddr) -> Result<(), Error>; -} diff --git a/src/handler/tests.rs b/src/handler/tests.rs index b82099010..75f5f7203 100644 --- a/src/handler/tests.rs +++ b/src/handler/tests.rs @@ -75,7 +75,7 @@ async fn build_handler_with_listen_config( let (service_send, handler_recv) = mpsc::channel(50); let (exit_tx, exit) = oneshot::channel(); - let nat_utils = NatUtils::new( + let nat = Nat::new( &listen_sockets, &enr, config.listen_config.ip_mode(), @@ -107,7 +107,7 @@ async fn build_handler_with_listen_config( service_send, listen_sockets, socket, - nat_utils, + nat, exit, }, MockService { @@ -191,9 +191,9 @@ async fn simple_session_message() { loop { if let Some(message) = receiver_recv.recv().await { match message { - HandlerOut::WhoAreYou(wru_ref) => { + HandlerOut::RequestEnr(EnrRequestData::WhoAreYou(wru_ref)) => { let _ = - recv_send.send(HandlerIn::WhoAreYou(wru_ref, Some(sender_enr.clone()))); + recv_send.send(HandlerIn::EnrResponse(Some(sender_enr.clone()), EnrRequestData::WhoAreYou(wru_ref))); } HandlerOut::Request(_, request) => { assert_eq!(request, send_message); @@ -307,8 +307,8 @@ async fn multiple_messages() { let receiver = async move { loop { match receiver_handler.recv().await { - Some(HandlerOut::WhoAreYou(wru_ref)) => { - let _ = recv_send.send(HandlerIn::WhoAreYou(wru_ref, Some(sender_enr.clone()))); + Some(HandlerOut::RequestEnr(EnrRequestData::WhoAreYou(wru_ref))) => { + let _ = recv_send.send(HandlerIn::EnrResponse(Some(sender_enr.clone()), EnrRequestData::WhoAreYou(wru_ref))); } Some(HandlerOut::Request(addr, request)) => { assert_eq!(request, recv_send_message); @@ -551,8 +551,8 @@ async fn nat_hole_punch_relay() { let mock_service_handle = tokio::spawn(async move { let service_msg = rx.recv().await.expect("should receive service message"); match service_msg { - HandlerOut::FindHolePunchEnr(relay_init) => tx - .send(HandlerIn::HolePunchEnr(tgt_enr_clone, relay_init)) + HandlerOut::RequestEnr(EnrRequestData::Nat(relay_init)) => tx + .send(HandlerIn::EnrResponse(Some(tgt_enr_clone), EnrRequestData::Nat(relay_init))) .expect("should send message to handler"), _ => panic!("service message should be 'find hole punch enr'"), } @@ -641,7 +641,7 @@ async fn nat_hole_punch_target() { build_handler_with_listen_config::(listen_config).await; let tgt_addr = handler.enr.read().udp4_socket().unwrap().into(); let tgt_node_id = handler.enr.read().node_id(); - handler.nat_utils.is_behind_nat = Some(true); + handler.nat.is_behind_nat = Some(true); // Relay let relay_enr = { diff --git a/src/service.rs b/src/service.rs index 783d175ca..3e3ecc1ac 100644 --- a/src/service.rs +++ b/src/service.rs @@ -19,7 +19,7 @@ use self::{ }; use crate::{ error::{RequestError, ResponseError}, - handler::{Handler, HandlerIn, HandlerOut}, + handler::{Handler, HandlerIn, HandlerOut, EnrRequestData}, kbucket::{ self, ConnectionDirection, ConnectionState, FailureReason, InsertResult, KBucketsTable, NodeStatus, UpdateResult, MAX_NODES_PER_BUCKET, @@ -387,71 +387,68 @@ impl Service { HandlerOut::Response(node_address, response) => { self.handle_rpc_response(node_address, *response); } - HandlerOut::WhoAreYou(whoareyou_ref) => { + HandlerOut::RequestEnr(EnrRequestData::WhoAreYou(whoareyou_ref)) => { // check what our latest known ENR is for this node. if let Some(known_enr) = self.find_enr(&whoareyou_ref.0.node_id) { - if let Err(e) = self.handler_send.send(HandlerIn::WhoAreYou(whoareyou_ref, Some(known_enr))) { + if let Err(e) = self.handler_send.send(HandlerIn::EnrResponse(Some(known_enr), EnrRequestData::WhoAreYou(whoareyou_ref))) { warn!("Failed to send whoareyou {}", e); }; } else { // do not know of this peer debug!("NodeId unknown, requesting ENR. {}", whoareyou_ref.0); - if let Err(e) = self.handler_send.send(HandlerIn::WhoAreYou(whoareyou_ref, None)) { + if let Err(e) = self.handler_send.send(HandlerIn::EnrResponse(None, EnrRequestData::WhoAreYou(whoareyou_ref))) { warn!("Failed to send who are you to unknown enr peer {}", e); } } } - HandlerOut::RequestFailed(request_id, error) => { - if let RequestError::Timeout = error { - debug!("RPC Request timed out. id: {}", request_id); - } else { - warn!("RPC Request failed: id: {}, error {:?}", request_id, error); - } - self.rpc_failure(request_id, error); - } - HandlerOut::FindHolePunchEnr(relay_init) => { - // update initiator's enr if it's in kbuckets - let inr_enr = relay_init.initiator_enr(); - let inr_key = kbucket::Key::from(inr_enr.node_id()); - match self.kbuckets.write().entry(&inr_key) { + HandlerOut::RequestEnr(EnrRequestData::Nat(relay_initiator)) => { + // Update initiator's Enr if it's in kbuckets + let initiator_enr = relay_initiator.initiator_enr(); + let initiator_key = kbucket::Key::from(initiator_enr.node_id()); + match self.kbuckets.write().entry(&initiator_key) { kbucket::Entry::Present(ref mut entry, _) => { let enr = entry.value_mut(); - if enr.seq() < inr_enr.seq() { - *enr = inr_enr.clone(); + if enr.seq() < initiator_enr.seq() { + *enr = initiator_enr.clone(); } } kbucket::Entry::Pending(ref mut entry, _) => { let enr = entry.value_mut(); - if enr.seq() < inr_enr.seq() { - *enr = inr_enr.clone(); + if enr.seq() < initiator_enr.seq() { + *enr = initiator_enr.clone(); } } _ => () } // check if we know the target node id in our routing table, otherwise // drop relay attempt. - let tgt_node_id = relay_init.target_node_id(); - let tgt_key = kbucket::Key::from(tgt_node_id); - if let kbucket::Entry::Present(entry, _) = self.kbuckets.write().entry(&tgt_key) { - let tgt_enr = entry.value().clone(); - if let Err(e) = self.handler_send.send(HandlerIn::HolePunchEnr(tgt_enr, relay_init)) { + let target_node_id = relay_initiator.target_node_id(); + let target_key = kbucket::Key::from(target_node_id); + if let kbucket::Entry::Present(entry, _) = self.kbuckets.write().entry(&target_key) { + let target_enr = entry.value().clone(); + if let Err(e) = self.handler_send.send(HandlerIn::EnrResponse(Some(target_enr), EnrRequestData::Nat(relay_initiator))) { warn!( "Failed to send target enr to relay process, error: {e}" ); } } else { - // todo(emhane): ban peers that ask us to relay to a peer we very - // unlikely could have sent to them in a NODES response. - let inr_node_id = relay_init.initiator_enr().node_id(); - + let initiator_node_id = relay_initiator.initiator_enr().node_id(); warn!( - inr_node_id=%inr_node_id, - tgt_node_id=%tgt_node_id, + initiator_node_id=%initiator_node_id, + target_node_id=%target_node_id, "Peer requested relaying to a peer not in k-buckets" ); } + }, + HandlerOut::PingAllPeers => self.ping_connected_peers(), + HandlerOut::RequestFailed(request_id, error) => { + if let RequestError::Timeout = error { + debug!("RPC Request timed out. id: {}", request_id); + } else { + warn!("RPC Request failed: id: {}, error {:?}", request_id, error); + } + self.rpc_failure(request_id, error); } - HandlerOut::PingAllPeers => self.ping_connected_peers() } } event = Service::bucket_maintenance_poll(&self.kbuckets) => { From 18e44f2fd3e7186fac783a97a784c7341e5adf02 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Wed, 17 Jan 2024 16:39:06 +1100 Subject: [PATCH 148/154] Old mate fmt --- src/handler/mod.rs | 39 ++++++++++++++++++++++++--------------- src/handler/tests.rs | 16 ++++++++++++---- src/service.rs | 2 +- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 3618ebf06..962f6d9e5 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -29,7 +29,7 @@ use crate::{ config::Discv5Config, discv5::PERMIT_BAN_LIST, - error::{Discv5Error, RequestError, NatError}, + error::{Discv5Error, NatError, RequestError}, packet::{ChallengeData, IdNonce, MessageNonce, Packet, PacketKind, ProtocolIdentity}, rpc::{ Message, Payload, RelayInitNotification, RelayMsgNotification, Request, RequestBody, @@ -937,9 +937,7 @@ impl Handler { let enr_not_reachable = !Nat::is_enr_reachable(&most_recent_enr); // Decide whether to establish this connection based on our appetite for unreachable - if enr_not_reachable - && Some(self.sessions.tagged()) >= self.nat.unreachable_enr_limit - { + if enr_not_reachable && Some(self.sessions.tagged()) >= self.nat.unreachable_enr_limit { debug!("Reached limit of unreachable ENR sessions. Avoiding a new connection. Limit: {}", self.sessions.tagged()); return; } @@ -1146,7 +1144,10 @@ impl Handler { let ban_timeout = self.nat.ban_duration.map(|v| Instant::now() + v); PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); } else if let Err(e) = self.on_relay_initiator(notification).await { - warn!("failed handling notification to relay for {node_address}, {:?}", e); + warn!( + "failed handling notification to relay for {node_address}, {:?}", + e + ); } } Message::RelayMsgNotification(notification) => { @@ -1158,7 +1159,10 @@ impl Handler { } _ => { if let Err(e) = self.on_relay_msg::

(notification).await { - warn!("failed handling notification relayed from {node_address}, {:?}", e); + warn!( + "failed handling notification relayed from {node_address}, {:?}", + e + ); } } } @@ -1211,7 +1215,9 @@ impl Handler { let whoareyou_ref = WhoAreYouRef(node_address, message_nonce); if let Err(e) = self .service_send - .send(HandlerOut::RequestEnr(EnrRequestData::WhoAreYou(whoareyou_ref))) + .send(HandlerOut::RequestEnr(EnrRequestData::WhoAreYou( + whoareyou_ref, + ))) .await { warn!("Failed to send WhoAreYou to the service {}", e) @@ -1257,7 +1263,9 @@ impl Handler { let whoareyou_ref = WhoAreYouRef(node_address, message_nonce); if let Err(e) = self .service_send - .send(HandlerOut::RequestEnr(EnrRequestData::WhoAreYou(whoareyou_ref))) + .send(HandlerOut::RequestEnr(EnrRequestData::WhoAreYou( + whoareyou_ref, + ))) .await { warn!( @@ -1586,7 +1594,6 @@ fn most_recent_enr(first: Option, second: Option) -> Result { } } - // NAT-related functions impl Handler { /// A request times out. Should trigger the initiation of a hole punch attempt, given a @@ -1636,7 +1643,10 @@ impl Handler { /// A RelayInit notification is received over discv5 indicating this node is the relay. Should /// trigger sending a RelayMsg to the target. - async fn on_relay_initiator(&mut self, relay_initiator: RelayInitNotification) -> Result<(), NatError> { + async fn on_relay_initiator( + &mut self, + relay_initiator: RelayInitNotification, + ) -> Result<(), NatError> { // Check for target peer in our kbuckets otherwise drop notification. if let Err(e) = self .service_send @@ -1655,11 +1665,10 @@ impl Handler { relay_msg: RelayMsgNotification, ) -> Result<(), NatError> { let (inr_enr, timed_out_msg_nonce) = relay_msg.into(); - let initiator_node_address = - match NodeContact::try_from_enr(inr_enr, self.nat.ip_mode) { - Ok(contact) => contact.node_address(), - Err(e) => return Err(NatError::Target(e.into())), - }; + let initiator_node_address = match NodeContact::try_from_enr(inr_enr, self.nat.ip_mode) { + Ok(contact) => contact.node_address(), + Err(e) => return Err(NatError::Target(e.into())), + }; // A session may already have been established. if self.sessions.get(&initiator_node_address).is_some() { diff --git a/src/handler/tests.rs b/src/handler/tests.rs index 75f5f7203..c2358c394 100644 --- a/src/handler/tests.rs +++ b/src/handler/tests.rs @@ -192,8 +192,10 @@ async fn simple_session_message() { if let Some(message) = receiver_recv.recv().await { match message { HandlerOut::RequestEnr(EnrRequestData::WhoAreYou(wru_ref)) => { - let _ = - recv_send.send(HandlerIn::EnrResponse(Some(sender_enr.clone()), EnrRequestData::WhoAreYou(wru_ref))); + let _ = recv_send.send(HandlerIn::EnrResponse( + Some(sender_enr.clone()), + EnrRequestData::WhoAreYou(wru_ref), + )); } HandlerOut::Request(_, request) => { assert_eq!(request, send_message); @@ -308,7 +310,10 @@ async fn multiple_messages() { loop { match receiver_handler.recv().await { Some(HandlerOut::RequestEnr(EnrRequestData::WhoAreYou(wru_ref))) => { - let _ = recv_send.send(HandlerIn::EnrResponse(Some(sender_enr.clone()), EnrRequestData::WhoAreYou(wru_ref))); + let _ = recv_send.send(HandlerIn::EnrResponse( + Some(sender_enr.clone()), + EnrRequestData::WhoAreYou(wru_ref), + )); } Some(HandlerOut::Request(addr, request)) => { assert_eq!(request, recv_send_message); @@ -552,7 +557,10 @@ async fn nat_hole_punch_relay() { let service_msg = rx.recv().await.expect("should receive service message"); match service_msg { HandlerOut::RequestEnr(EnrRequestData::Nat(relay_init)) => tx - .send(HandlerIn::EnrResponse(Some(tgt_enr_clone), EnrRequestData::Nat(relay_init))) + .send(HandlerIn::EnrResponse( + Some(tgt_enr_clone), + EnrRequestData::Nat(relay_init), + )) .expect("should send message to handler"), _ => panic!("service message should be 'find hole punch enr'"), } diff --git a/src/service.rs b/src/service.rs index 3e3ecc1ac..8d7cc2eb7 100644 --- a/src/service.rs +++ b/src/service.rs @@ -19,7 +19,7 @@ use self::{ }; use crate::{ error::{RequestError, ResponseError}, - handler::{Handler, HandlerIn, HandlerOut, EnrRequestData}, + handler::{EnrRequestData, Handler, HandlerIn, HandlerOut}, kbucket::{ self, ConnectionDirection, ConnectionState, FailureReason, InsertResult, KBucketsTable, NodeStatus, UpdateResult, MAX_NODES_PER_BUCKET, From e3478c2d7c4021b2eeb2e88be61ba9ccd2297cd5 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 17 Jan 2024 16:26:01 +0100 Subject: [PATCH 149/154] Fix bug broken invariant active-requests and hole punch attempts --- src/error.rs | 7 ++++++- src/handler/mod.rs | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index c6323dffb..e0c9fb4e2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,7 @@ -use crate::{handler::Challenge, node_info::NonContactable}; +use crate::{ + handler::Challenge, + node_info::{NodeAddress, NonContactable}, +}; use derive_more::From; use rlp::DecoderError; use std::fmt; @@ -33,6 +36,8 @@ pub enum Discv5Error { ServiceAlreadyStarted, /// A session could not be established with the remote. SessionNotEstablished, + /// A session to the given peer is already established. + SessionAlreadyEstablished(NodeAddress), /// An RLP decoding error occurred. RLPError(DecoderError), /// Failed to encrypt a message. diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 0d108ea25..745747ade 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -553,6 +553,11 @@ impl Handler { .on_request_time_out::

(relay, local_enr, nonce, target) .await { + Err(NatError::Initiator(Discv5Error::SessionAlreadyEstablished( + node_address, + ))) => { + debug!("Session to peer already established, aborting hole punch attempt. Peer: {node_address}"); + } Err(e) => { warn!("Failed to start hole punching. Error: {:?}", e); } @@ -1591,7 +1596,9 @@ impl HolePunchNat for Handler { ) -> Result<(), NatError> { // Another hole punch process with this target may have just completed. if self.sessions.get(&target_node_address).is_some() { - return Ok(()); + return Err(NatError::Initiator(Discv5Error::SessionAlreadyEstablished( + target_node_address, + ))); } if let Some(session) = self.sessions.get_mut(&relay) { let relay_init_notif = From b42e6776bd8ea6465b890dd10940d7a1f985a65d Mon Sep 17 00:00:00 2001 From: Age Manning Date: Thu, 18 Jan 2024 11:26:07 +1100 Subject: [PATCH 150/154] Update src/handler/mod.rs Co-authored-by: Emilia Hane --- src/handler/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 194961c51..9a4b44a44 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1650,7 +1650,7 @@ impl Handler { /// A RelayInit notification is received over discv5 indicating this node is the relay. Should /// trigger sending a RelayMsg to the target. - async fn on_relay_initiator( + async fn on_relay_initiation( &mut self, relay_initiator: RelayInitNotification, ) -> Result<(), NatError> { From bf4723db2ba9951b875796c8308d109f6a6e0347 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Thu, 18 Jan 2024 11:26:13 +1100 Subject: [PATCH 151/154] Update src/service.rs Co-authored-by: Emilia Hane --- src/service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service.rs b/src/service.rs index 8d7cc2eb7..75257c0a6 100644 --- a/src/service.rs +++ b/src/service.rs @@ -401,7 +401,7 @@ impl Service { } } } - HandlerOut::RequestEnr(EnrRequestData::Nat(relay_initiator)) => { + HandlerOut::RequestEnr(EnrRequestData::Nat(relay_initiation)) => { // Update initiator's Enr if it's in kbuckets let initiator_enr = relay_initiator.initiator_enr(); let initiator_key = kbucket::Key::from(initiator_enr.node_id()); From 942661c01567d0d591060c304949693c87986684 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Thu, 18 Jan 2024 11:26:19 +1100 Subject: [PATCH 152/154] Update src/handler/mod.rs Co-authored-by: Emilia Hane --- src/handler/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 9a4b44a44..740606d45 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -381,7 +381,7 @@ impl Handler { } HandlerIn::Response(dst, response) => self.send_response::

(dst, *response).await, HandlerIn::EnrResponse(enr, EnrRequestData::WhoAreYou(wru_ref)) => self.send_challenge::

(wru_ref, enr).await, - HandlerIn::EnrResponse(Some(target_enr), EnrRequestData::Nat(relay_initiator)) => { + HandlerIn::EnrResponse(Some(target_enr), EnrRequestData::Nat(relay_initiation)) => { // Assemble the notification for the target let (initiator_enr, _target, timed_out_nonce) = relay_initiator.into(); let relay_msg_notification = RelayMsgNotification::new(initiator_enr, timed_out_nonce); From 08cd2b6dadcbe5ac93df8f2ef631ecfc440ab33f Mon Sep 17 00:00:00 2001 From: Age Manning Date: Thu, 18 Jan 2024 11:47:19 +1100 Subject: [PATCH 153/154] Complete the renaming --- src/handler/mod.rs | 8 ++++---- src/service.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 740606d45..1f695fbf3 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -383,7 +383,7 @@ impl Handler { HandlerIn::EnrResponse(enr, EnrRequestData::WhoAreYou(wru_ref)) => self.send_challenge::

(wru_ref, enr).await, HandlerIn::EnrResponse(Some(target_enr), EnrRequestData::Nat(relay_initiation)) => { // Assemble the notification for the target - let (initiator_enr, _target, timed_out_nonce) = relay_initiator.into(); + let (initiator_enr, _target, timed_out_nonce) = relay_initiation.into(); let relay_msg_notification = RelayMsgNotification::new(initiator_enr, timed_out_nonce); if let Err(e) = self.send_relay_msg_notification::

(target_enr, relay_msg_notification).await { warn!("Failed to relay. Error: {:?}", e); @@ -1148,7 +1148,7 @@ impl Handler { .await; let ban_timeout = self.nat.ban_duration.map(|v| Instant::now() + v); PERMIT_BAN_LIST.write().ban(node_address, ban_timeout); - } else if let Err(e) = self.on_relay_initiator(notification).await { + } else if let Err(e) = self.on_relay_initiation(notification).await { warn!( "failed handling notification to relay for {node_address}, {:?}", e @@ -1652,12 +1652,12 @@ impl Handler { /// trigger sending a RelayMsg to the target. async fn on_relay_initiation( &mut self, - relay_initiator: RelayInitNotification, + relay_initiation: RelayInitNotification, ) -> Result<(), NatError> { // Check for target peer in our kbuckets otherwise drop notification. if let Err(e) = self .service_send - .send(HandlerOut::RequestEnr(EnrRequestData::Nat(relay_initiator))) + .send(HandlerOut::RequestEnr(EnrRequestData::Nat(relay_initiation))) .await { return Err(NatError::Relay(e.into())); diff --git a/src/service.rs b/src/service.rs index 75257c0a6..f55089010 100644 --- a/src/service.rs +++ b/src/service.rs @@ -403,7 +403,7 @@ impl Service { } HandlerOut::RequestEnr(EnrRequestData::Nat(relay_initiation)) => { // Update initiator's Enr if it's in kbuckets - let initiator_enr = relay_initiator.initiator_enr(); + let initiator_enr = relay_initiation.initiator_enr(); let initiator_key = kbucket::Key::from(initiator_enr.node_id()); match self.kbuckets.write().entry(&initiator_key) { kbucket::Entry::Present(ref mut entry, _) => { @@ -422,17 +422,17 @@ impl Service { } // check if we know the target node id in our routing table, otherwise // drop relay attempt. - let target_node_id = relay_initiator.target_node_id(); + let target_node_id = relay_initiation.target_node_id(); let target_key = kbucket::Key::from(target_node_id); if let kbucket::Entry::Present(entry, _) = self.kbuckets.write().entry(&target_key) { let target_enr = entry.value().clone(); - if let Err(e) = self.handler_send.send(HandlerIn::EnrResponse(Some(target_enr), EnrRequestData::Nat(relay_initiator))) { + if let Err(e) = self.handler_send.send(HandlerIn::EnrResponse(Some(target_enr), EnrRequestData::Nat(relay_initiation))) { warn!( "Failed to send target enr to relay process, error: {e}" ); } } else { - let initiator_node_id = relay_initiator.initiator_enr().node_id(); + let initiator_node_id = relay_initiation.initiator_enr().node_id(); warn!( initiator_node_id=%initiator_node_id, target_node_id=%target_node_id, From fb54740f1f3ad4e431b8c9332fa40683b289dde6 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Thu, 18 Jan 2024 11:47:50 +1100 Subject: [PATCH 154/154] Fmt --- src/handler/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 1f695fbf3..74a54f348 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1657,7 +1657,9 @@ impl Handler { // Check for target peer in our kbuckets otherwise drop notification. if let Err(e) = self .service_send - .send(HandlerOut::RequestEnr(EnrRequestData::Nat(relay_initiation))) + .send(HandlerOut::RequestEnr(EnrRequestData::Nat( + relay_initiation, + ))) .await { return Err(NatError::Relay(e.into()));