/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
use std::sync::Arc;
use darkfi_serial::{serialize, SerialDecodable, SerialEncodable};
use log::{debug, error, info};
use rand::{rngs::OsRng, Rng};
use smol::{
io::{self, ReadHalf, WriteHalf},
lock::Mutex,
Executor,
};
use url::Url;
use super::{
dnet::{self, dnetev, DnetEvent},
message,
message::Packet,
message_subscriber::{MessageSubscription, MessageSubsystem},
p2p::P2pPtr,
session::{Session, SessionBitFlag, SessionWeakPtr},
transport::PtStream,
};
use crate::{
system::{StoppableTask, StoppableTaskPtr, Subscriber, SubscriberPtr, Subscription},
util::time::NanoTimestamp,
Error, Result,
};
/// Atomic pointer to async channel
pub type ChannelPtr = Arc;
/// Channel debug info
#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
pub struct ChannelInfo {
pub addr: Url,
pub id: u32,
}
impl ChannelInfo {
fn new(addr: Url) -> Self {
Self { addr, id: OsRng.gen() }
}
}
/// Async channel for communication between nodes.
pub struct Channel {
/// The reading half of the transport stream
reader: Mutex>>,
/// The writing half of the transport stream
writer: Mutex>>,
/// The message subsystem instance for this channel
message_subsystem: MessageSubsystem,
/// Subscriber listening for stop signal for closing this channel
stop_subscriber: SubscriberPtr,
/// Task that is listening for the stop signal
receive_task: StoppableTaskPtr,
/// A boolean marking if this channel is stopped
stopped: Mutex,
/// Weak pointer to respective session
session: SessionWeakPtr,
/// Channel debug info
pub info: ChannelInfo,
}
impl std::fmt::Debug for Channel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.address())
}
}
impl Channel {
/// Sets up a new channel. Creates a reader and writer [`PtStream`] and
/// summons the message subscriber subsystem. Performs a network handshake
/// on the subsystem dispatchers.
pub async fn new(stream: Box, addr: Url, session: SessionWeakPtr) -> Arc {
let (reader, writer) = io::split(stream);
let reader = Mutex::new(reader);
let writer = Mutex::new(writer);
let message_subsystem = MessageSubsystem::new();
Self::setup_dispatchers(&message_subsystem).await;
let info = ChannelInfo::new(addr.clone());
Arc::new(Self {
reader,
writer,
message_subsystem,
stop_subscriber: Subscriber::new(),
receive_task: StoppableTask::new(),
stopped: Mutex::new(false),
session,
info,
})
}
/// Perform network handshake for message subsystem dispatchers.
async fn setup_dispatchers(subsystem: &MessageSubsystem) {
subsystem.add_dispatch::().await;
subsystem.add_dispatch::().await;
subsystem.add_dispatch::().await;
subsystem.add_dispatch::().await;
subsystem.add_dispatch::().await;
subsystem.add_dispatch::().await;
}
/// Starts the channel. Runs a receive loop to start receiving messages
/// or handles a network failure.
pub fn start(self: Arc, executor: Arc>) {
debug!(target: "net::channel::start()", "START => address={}", self.address());
let self_ = self.clone();
self.receive_task.clone().start(
self.clone().main_receive_loop(),
|result| self_.handle_stop(result),
Error::NetworkServiceStopped,
executor,
);
debug!(target: "net::channel::start()", "END => address={}", self.address());
}
/// Stops the channel. Steps through each component of the channel connection
/// and sends a stop signal. Notifies all subscribers that the channel has
/// been closed.
pub async fn stop(&self) {
debug!(target: "net::channel::stop()", "START => address={}", self.address());
if !*self.stopped.lock().await {
*self.stopped.lock().await = true;
self.stop_subscriber.notify(Error::ChannelStopped).await;
self.receive_task.stop().await;
self.message_subsystem.trigger_error(Error::ChannelStopped).await;
}
debug!(target: "net::channel::stop()", "END => address={}", self.address());
}
/// Creates a subscription to a stopped signal.
/// If the channel is stopped then this will return a ChannelStopped error.
pub async fn subscribe_stop(&self) -> Result> {
debug!(target: "net::channel::subscribe_stop()", "START => address={}", self.address());
if *self.stopped.lock().await {
return Err(Error::ChannelStopped)
}
let sub = self.stop_subscriber.clone().subscribe().await;
debug!(target: "net::channel::subscribe_stop()", "END => address={}", self.address());
Ok(sub)
}
/// Sends a message across a channel. Calls `send_message` that creates
/// a new payload and sends it over the network transport as a packet.
/// Returns an error if something goes wrong.
pub async fn send(&self, message: &M) -> Result<()> {
debug!(
target: "net::channel::send()", "[START] command={} => address={}",
M::NAME, self.address(),
);
if *self.stopped.lock().await {
return Err(Error::ChannelStopped)
}
// Catch failure and stop channel, return a net error
if let Err(e) = self.send_message(message).await {
error!(
target: "net::channel::send()", "[P2P] Channel send error for [{}]: {}",
self.address(), e
);
self.stop().await;
return Err(Error::ChannelStopped)
}
debug!(
target: "net::channel::send()", "[END] command={} => address={}",
M::NAME,self.address(),
);
Ok(())
}
/// Implements send message functionality. Creates a new payload and
/// encodes it. Then creates a message packet (the base type of the
/// network) and copies the payload into it. Then we send the packet
/// over the network stream.
async fn send_message(&self, message: &M) -> Result<()> {
let packet = Packet { command: M::NAME.to_string(), payload: serialize(message) };
dnetev!(self, SendMessage, {
chan: self.info.clone(),
cmd: packet.command.clone(),
time: NanoTimestamp::current_time(),
});
let stream = &mut *self.writer.lock().await;
let _ = message::send_packet(stream, packet).await?;
Ok(())
}
/// Subscribe to a message on the message subsystem.
pub async fn subscribe_msg(&self) -> Result> {
debug!(
target: "net::channel::subscribe_msg()", "[START] command={} => address={}",
M::NAME, self.address(),
);
let sub = self.message_subsystem.subscribe::().await;
debug!(
target: "net::channel::subscribe_msg()", "[END] command={} => address={}",
M::NAME, self.address(),
);
sub
}
/// Handle network errors. Panic if error passes silently, otherwise
/// broadcast the error.
async fn handle_stop(self: Arc, result: Result<()>) {
debug!(target: "net::channel::handle_stop()", "[START] address={}", self.address());
match result {
Ok(()) => panic!("Channel task should never complete without error status"),
// Send this error to all channel subscribers
Err(e) => self.message_subsystem.trigger_error(e).await,
}
debug!(target: "net::channel::handle_stop()", "[END] address={}", self.address());
}
/// Run the receive loop. Start receiving messages or handle network failure.
async fn main_receive_loop(self: Arc) -> Result<()> {
debug!(target: "net::channel::main_receive_loop()", "[START] address={}", self.address());
// Acquire reader lock
let reader = &mut *self.reader.lock().await;
// Run loop
loop {
let packet = match message::read_packet(reader).await {
Ok(packet) => packet,
Err(err) => {
if Self::is_eof_error(&err) {
info!(
target: "net::channel::main_receive_loop()",
"[P2P] Channel inbound connection {} disconnected",
self.address(),
);
} else {
error!(
target: "net::channel::main_receive_loop()",
"[P2P] Read error on channel {}: {}",
self.address(), err,
);
}
debug!(
target: "net::channel::main_receive_loop()",
"Stopping channel {}", self.address(),
);
self.stop().await;
return Err(Error::ChannelStopped)
}
};
dnetev!(self, RecvMessage, {
chan: self.info.clone(),
cmd: packet.command.clone(),
time: NanoTimestamp::current_time(),
});
// Send result to our subscribers
self.message_subsystem.notify(&packet.command, &packet.payload).await;
}
}
/// Returns the local socket address
pub fn address(&self) -> &Url {
&self.info.addr
}
/// Returns the inner [`MessageSubsystem`] reference
pub fn message_subsystem(&self) -> &MessageSubsystem {
&self.message_subsystem
}
fn session(&self) -> Arc {
self.session.upgrade().unwrap()
}
pub fn session_type_id(&self) -> SessionBitFlag {
let session = self.session();
session.type_id()
}
fn p2p(&self) -> P2pPtr {
self.session().p2p()
}
fn is_eof_error(err: &Error) -> bool {
match err {
Error::Io(ioerr) => ioerr == &std::io::ErrorKind::UnexpectedEof,
_ => false,
}
}
}