mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(net): add active session type (#169)
* refactor(net): unify capability types * refactor(net): unify capability types * feat(net): add active session type
This commit is contained in:
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
use reth_eth_wire::{BlockHeaders, GetBlockHeaders};
|
use reth_eth_wire::{BlockHeaders, GetBlockHeaders};
|
||||||
|
|
||||||
|
use crate::NodeId;
|
||||||
|
use reth_eth_wire::capability::CapabilityMessage;
|
||||||
use tokio::sync::{mpsc, oneshot};
|
use tokio::sync::{mpsc, oneshot};
|
||||||
|
|
||||||
/// Result alias for result of a request.
|
/// Result alias for result of a request.
|
||||||
@ -53,3 +55,12 @@ pub enum CapabilityRequest {
|
|||||||
pub enum CapabilityResponse {
|
pub enum CapabilityResponse {
|
||||||
GetBlockHeaders(RequestResult<BlockHeaders>),
|
GetBlockHeaders(RequestResult<BlockHeaders>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A Cloneable connection for sending messages directly to the session of a peer.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PeerMessageSender {
|
||||||
|
/// id of the remote node.
|
||||||
|
pub(crate) peer: NodeId,
|
||||||
|
/// The Sender half connected to a session.
|
||||||
|
pub(crate) to_session_tx: mpsc::Sender<CapabilityMessage>,
|
||||||
|
}
|
||||||
|
|||||||
114
crates/net/network/src/session/active.rs
Normal file
114
crates/net/network/src/session/active.rs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
//! Represents an established session.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
session::{
|
||||||
|
handle::{ActiveSessionMessage, SessionCommand},
|
||||||
|
SessionId,
|
||||||
|
},
|
||||||
|
NodeId,
|
||||||
|
};
|
||||||
|
|
||||||
|
use fnv::FnvHashMap;
|
||||||
|
use futures::{stream::Fuse, Sink, Stream};
|
||||||
|
use pin_project::pin_project;
|
||||||
|
use reth_ecies::stream::ECIESStream;
|
||||||
|
use reth_eth_wire::capability::{Capabilities, CapabilityMessage};
|
||||||
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
future::Future,
|
||||||
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
use tokio::{net::TcpStream, sync::mpsc};
|
||||||
|
use tokio_stream::wrappers::ReceiverStream;
|
||||||
|
|
||||||
|
/// The type that advances an established session by listening for incoming messages (from local
|
||||||
|
/// node or read from connection) and emitting events back to the [`SessionHandler`].
|
||||||
|
#[pin_project]
|
||||||
|
pub(crate) struct ActiveSession {
|
||||||
|
/// The underlying connection.
|
||||||
|
#[pin]
|
||||||
|
pub(crate) conn: ECIESStream<TcpStream>,
|
||||||
|
/// Identifier of the node we're connected to.
|
||||||
|
pub(crate) remote_node_id: NodeId,
|
||||||
|
/// All capabilities the peer announced
|
||||||
|
pub(crate) remote_capabilities: Arc<Capabilities>,
|
||||||
|
/// Internal identifier of this session
|
||||||
|
pub(crate) session_id: SessionId,
|
||||||
|
/// Incoming commands from the manager
|
||||||
|
#[pin]
|
||||||
|
pub(crate) commands_rx: ReceiverStream<SessionCommand>,
|
||||||
|
/// Sink to send messages to the [`SessionManager`].
|
||||||
|
pub(crate) to_session: mpsc::Sender<ActiveSessionMessage>,
|
||||||
|
/// Incoming request to send to delegate to the remote peer.
|
||||||
|
#[pin]
|
||||||
|
pub(crate) messages_rx: Fuse<ReceiverStream<CapabilityMessage>>,
|
||||||
|
/// All requests currently in progress.
|
||||||
|
pub(crate) inflight_requests: FnvHashMap<u64, ()>,
|
||||||
|
/// Buffered messages that should be sent to the remote peer.
|
||||||
|
pub(crate) buffered_outgoing: VecDeque<CapabilityMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for ActiveSession {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut this = self.project();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut progress = false;
|
||||||
|
// we prioritize incoming messages
|
||||||
|
loop {
|
||||||
|
match this.commands_rx.as_mut().poll_next(cx) {
|
||||||
|
Poll::Pending => break,
|
||||||
|
Poll::Ready(None) => {
|
||||||
|
// this is only possible when the manager was dropped, in which case we also
|
||||||
|
// terminate this session
|
||||||
|
return Poll::Ready(())
|
||||||
|
}
|
||||||
|
Poll::Ready(Some(_cmd)) => {
|
||||||
|
progress = true;
|
||||||
|
// TODO handle command
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Poll::Ready(Some(_msg)) = this.messages_rx.as_mut().poll_next(cx) {
|
||||||
|
progress = true;
|
||||||
|
// TODO handle request
|
||||||
|
}
|
||||||
|
|
||||||
|
// send and flush
|
||||||
|
while this.conn.as_mut().poll_ready(cx).is_ready() {
|
||||||
|
if let Some(_msg) = this.buffered_outgoing.pop_front() {
|
||||||
|
progress = true;
|
||||||
|
// TODO encode message and start send
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match this.conn.as_mut().poll_next(cx) {
|
||||||
|
Poll::Pending => break,
|
||||||
|
Poll::Ready(None) => {
|
||||||
|
// disconnected
|
||||||
|
}
|
||||||
|
Poll::Ready(Some(_msg)) => {
|
||||||
|
progress = true;
|
||||||
|
// decode and handle message
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !progress {
|
||||||
|
return Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,7 +4,10 @@ use crate::{
|
|||||||
NodeId,
|
NodeId,
|
||||||
};
|
};
|
||||||
use reth_ecies::{stream::ECIESStream, ECIESError};
|
use reth_ecies::{stream::ECIESStream, ECIESError};
|
||||||
use reth_eth_wire::capability::{Capabilities, CapabilityMessage};
|
use reth_eth_wire::{
|
||||||
|
capability::{Capabilities, CapabilityMessage},
|
||||||
|
Status,
|
||||||
|
};
|
||||||
use std::{io, net::SocketAddr, sync::Arc, time::Instant};
|
use std::{io, net::SocketAddr, sync::Arc, time::Instant};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
@ -36,7 +39,7 @@ pub(crate) struct ActiveSessionHandle {
|
|||||||
/// Announced capabilities of the peer.
|
/// Announced capabilities of the peer.
|
||||||
pub(crate) capabilities: Arc<Capabilities>,
|
pub(crate) capabilities: Arc<Capabilities>,
|
||||||
/// Sender half of the command channel used send commands _to_ the spawned session
|
/// Sender half of the command channel used send commands _to_ the spawned session
|
||||||
pub(crate) commands: mpsc::Sender<SessionCommand>,
|
pub(crate) commands_to_session: mpsc::Sender<SessionCommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// === impl ActiveSessionHandle ===
|
// === impl ActiveSessionHandle ===
|
||||||
@ -45,7 +48,7 @@ impl ActiveSessionHandle {
|
|||||||
/// Sends a disconnect command to the session.
|
/// Sends a disconnect command to the session.
|
||||||
pub(crate) fn disconnect(&self) {
|
pub(crate) fn disconnect(&self) {
|
||||||
// Note: we clone the sender which ensures the channel has capacity to send the message
|
// Note: we clone the sender which ensures the channel has capacity to send the message
|
||||||
let _ = self.commands.clone().try_send(SessionCommand::Disconnect);
|
let _ = self.commands_to_session.clone().try_send(SessionCommand::Disconnect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,12 +61,14 @@ impl ActiveSessionHandle {
|
|||||||
pub(crate) enum PendingSessionEvent {
|
pub(crate) enum PendingSessionEvent {
|
||||||
/// Initial handshake step was successful <https://github.com/ethereum/devp2p/blob/6b0abc3d956a626c28dce1307ee9f546db17b6bd/rlpx.md#initial-handshake>
|
/// Initial handshake step was successful <https://github.com/ethereum/devp2p/blob/6b0abc3d956a626c28dce1307ee9f546db17b6bd/rlpx.md#initial-handshake>
|
||||||
SuccessfulHandshake { remote_addr: SocketAddr, session_id: SessionId },
|
SuccessfulHandshake { remote_addr: SocketAddr, session_id: SessionId },
|
||||||
/// Represents a successful `Hello` exchange: <https://github.com/ethereum/devp2p/blob/6b0abc3d956a626c28dce1307ee9f546db17b6bd/rlpx.md#hello-0x00>
|
/// Represents a successful `Hello` and `Status` exchange: <https://github.com/ethereum/devp2p/blob/6b0abc3d956a626c28dce1307ee9f546db17b6bd/rlpx.md#hello-0x00>
|
||||||
Hello {
|
Established {
|
||||||
session_id: SessionId,
|
session_id: SessionId,
|
||||||
|
remote_addr: SocketAddr,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
capabilities: Arc<Capabilities>,
|
capabilities: Arc<Capabilities>,
|
||||||
stream: ECIESStream<TcpStream>,
|
status: Status,
|
||||||
|
conn: ECIESStream<TcpStream>,
|
||||||
},
|
},
|
||||||
/// Handshake unsuccessful, session was disconnected.
|
/// Handshake unsuccessful, session was disconnected.
|
||||||
Disconnected {
|
Disconnected {
|
||||||
@ -114,12 +119,3 @@ pub(crate) enum ActiveSessionMessage {
|
|||||||
message: CapabilityMessage,
|
message: CapabilityMessage,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Cloneable connection for sending messages directly to the session of a peer.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PeerMessageSender {
|
|
||||||
/// id of the remote node.
|
|
||||||
pub(crate) peer: NodeId,
|
|
||||||
/// The Sender half connected to a session.
|
|
||||||
pub(crate) to_session_tx: mpsc::Sender<CapabilityMessage>,
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,17 +1,20 @@
|
|||||||
//! Support for handling peer sessions.
|
//! Support for handling peer sessions.
|
||||||
|
pub use crate::message::PeerMessageSender;
|
||||||
use crate::{
|
use crate::{
|
||||||
session::handle::{
|
session::{
|
||||||
ActiveSessionHandle, ActiveSessionMessage, PendingSessionEvent, PendingSessionHandle,
|
active::ActiveSession,
|
||||||
|
handle::{
|
||||||
|
ActiveSessionHandle, ActiveSessionMessage, PendingSessionEvent, PendingSessionHandle,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
NodeId,
|
NodeId,
|
||||||
};
|
};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use futures::{future::Either, io, FutureExt, StreamExt};
|
use futures::{future::Either, io, FutureExt, StreamExt};
|
||||||
pub use handle::PeerMessageSender;
|
|
||||||
use reth_ecies::{stream::ECIESStream, ECIESError};
|
use reth_ecies::{stream::ECIESStream, ECIESError};
|
||||||
use reth_eth_wire::{
|
use reth_eth_wire::{
|
||||||
capability::{Capabilities, CapabilityMessage},
|
capability::{Capabilities, CapabilityMessage},
|
||||||
UnauthedEthStream,
|
Status, UnauthedEthStream,
|
||||||
};
|
};
|
||||||
use secp256k1::{SecretKey, SECP256K1};
|
use secp256k1::{SecretKey, SECP256K1};
|
||||||
use std::{
|
use std::{
|
||||||
@ -20,6 +23,7 @@ use std::{
|
|||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
@ -29,6 +33,7 @@ use tokio::{
|
|||||||
use tokio_stream::wrappers::ReceiverStream;
|
use tokio_stream::wrappers::ReceiverStream;
|
||||||
use tracing::{instrument, trace, warn};
|
use tracing::{instrument, trace, warn};
|
||||||
|
|
||||||
|
mod active;
|
||||||
mod handle;
|
mod handle;
|
||||||
|
|
||||||
/// Internal identifier for active sessions.
|
/// Internal identifier for active sessions.
|
||||||
@ -213,29 +218,55 @@ impl SessionManager {
|
|||||||
"successful handshake"
|
"successful handshake"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
PendingSessionEvent::Hello {
|
PendingSessionEvent::Established {
|
||||||
session_id,
|
session_id,
|
||||||
node_id: _,
|
remote_addr,
|
||||||
capabilities: _,
|
node_id,
|
||||||
stream: _,
|
capabilities,
|
||||||
|
conn,
|
||||||
|
status,
|
||||||
} => {
|
} => {
|
||||||
// move from pending to established.
|
// move from pending to established.
|
||||||
let _ = self.pending_sessions.remove(&session_id);
|
let _ = self.pending_sessions.remove(&session_id);
|
||||||
|
|
||||||
// TODO spawn the authenticated session
|
let (commands_to_session, commands_rx) =
|
||||||
// let session = ActiveSessionHandle {
|
mpsc::channel(self.session_command_buffer);
|
||||||
// session_id,
|
|
||||||
// remote_id: node_id,
|
let (to_session_tx, messages_rx) = mpsc::channel(self.session_command_buffer);
|
||||||
// established: Instant::now(),
|
|
||||||
// capabilities,
|
let messages = PeerMessageSender { peer: node_id, to_session_tx };
|
||||||
// commands
|
|
||||||
// };
|
let session = ActiveSession {
|
||||||
// self.active_sessions.insert(node_id, session);
|
remote_node_id: node_id,
|
||||||
// return Poll::Ready(SessionEvent::SessionAuthenticated {
|
remote_capabilities: Arc::clone(&capabilities),
|
||||||
// node_id,
|
session_id,
|
||||||
// capabilities,
|
commands_rx: ReceiverStream::new(commands_rx),
|
||||||
// messages: ()
|
to_session: self.active_session_tx.clone(),
|
||||||
// })
|
messages_rx: ReceiverStream::new(messages_rx).fuse(),
|
||||||
|
inflight_requests: Default::default(),
|
||||||
|
conn,
|
||||||
|
buffered_outgoing: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.spawn(session);
|
||||||
|
|
||||||
|
let handle = ActiveSessionHandle {
|
||||||
|
session_id,
|
||||||
|
remote_id: node_id,
|
||||||
|
established: Instant::now(),
|
||||||
|
capabilities: Arc::clone(&capabilities),
|
||||||
|
commands_to_session,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.active_sessions.insert(node_id, handle);
|
||||||
|
|
||||||
|
return Poll::Ready(SessionEvent::SessionEstablished {
|
||||||
|
node_id,
|
||||||
|
remote_addr,
|
||||||
|
capabilities,
|
||||||
|
status,
|
||||||
|
messages,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
PendingSessionEvent::Disconnected { remote_addr, session_id, direction, error } => {
|
PendingSessionEvent::Disconnected { remote_addr, session_id, direction, error } => {
|
||||||
trace!(
|
trace!(
|
||||||
@ -343,10 +374,11 @@ pub(crate) enum SessionEvent {
|
|||||||
/// A new session was successfully authenticated.
|
/// A new session was successfully authenticated.
|
||||||
///
|
///
|
||||||
/// This session is now able to exchange data.
|
/// This session is now able to exchange data.
|
||||||
SessionAuthenticated {
|
SessionEstablished {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
remote_addr: SocketAddr,
|
remote_addr: SocketAddr,
|
||||||
capabilities: Arc<Capabilities>,
|
capabilities: Arc<Capabilities>,
|
||||||
|
status: Status,
|
||||||
messages: PeerMessageSender,
|
messages: PeerMessageSender,
|
||||||
},
|
},
|
||||||
/// A session received a valid message via RLPx.
|
/// A session received a valid message via RLPx.
|
||||||
|
|||||||
@ -5,12 +5,12 @@ use crate::{
|
|||||||
fetch::StateFetcher,
|
fetch::StateFetcher,
|
||||||
message::CapabilityResponse,
|
message::CapabilityResponse,
|
||||||
peers::{PeerAction, PeersManager},
|
peers::{PeerAction, PeersManager},
|
||||||
session::PeerMessageSender,
|
|
||||||
NodeId,
|
NodeId,
|
||||||
};
|
};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
|
||||||
use reth_eth_wire::capability::Capabilities;
|
use crate::message::PeerMessageSender;
|
||||||
|
use reth_eth_wire::{capability::Capabilities, Status};
|
||||||
use reth_interfaces::provider::BlockProvider;
|
use reth_interfaces::provider::BlockProvider;
|
||||||
use reth_primitives::{H256, U256};
|
use reth_primitives::{H256, U256};
|
||||||
use std::{
|
use std::{
|
||||||
@ -75,6 +75,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
_node_id: NodeId,
|
_node_id: NodeId,
|
||||||
_capabilities: Arc<Capabilities>,
|
_capabilities: Arc<Capabilities>,
|
||||||
|
_status: Status,
|
||||||
_messages: PeerMessageSender,
|
_messages: PeerMessageSender,
|
||||||
) {
|
) {
|
||||||
// TODO notify fetecher as well
|
// TODO notify fetecher as well
|
||||||
|
|||||||
@ -62,8 +62,14 @@ where
|
|||||||
/// Handles a polled [`SessionEvent`]
|
/// Handles a polled [`SessionEvent`]
|
||||||
fn on_session_event(&mut self, event: SessionEvent) -> Option<SwarmEvent> {
|
fn on_session_event(&mut self, event: SessionEvent) -> Option<SwarmEvent> {
|
||||||
match event {
|
match event {
|
||||||
SessionEvent::SessionAuthenticated { node_id, remote_addr, capabilities, messages } => {
|
SessionEvent::SessionEstablished {
|
||||||
self.state.on_session_authenticated(node_id, capabilities, messages);
|
node_id,
|
||||||
|
remote_addr,
|
||||||
|
capabilities,
|
||||||
|
status,
|
||||||
|
messages,
|
||||||
|
} => {
|
||||||
|
self.state.on_session_authenticated(node_id, capabilities, status, messages);
|
||||||
Some(SwarmEvent::SessionEstablished { node_id, remote_addr })
|
Some(SwarmEvent::SessionEstablished { node_id, remote_addr })
|
||||||
}
|
}
|
||||||
SessionEvent::ValidMessage { node_id, message } => {
|
SessionEvent::ValidMessage { node_id, message } => {
|
||||||
|
|||||||
Reference in New Issue
Block a user