mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
183 lines
5.0 KiB
Rust
183 lines
5.0 KiB
Rust
/* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable, VarInt};
|
|
use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
|
use log::debug;
|
|
use url::Url;
|
|
|
|
use crate::{Error, Result};
|
|
|
|
const MAGIC_BYTES: [u8; 4] = [0xd9, 0xef, 0xb6, 0x7d];
|
|
|
|
/// Generic message template.
|
|
pub trait Message: 'static + Encodable + Decodable + Send + Sync {
|
|
fn name() -> &'static str;
|
|
}
|
|
|
|
/// Outbound keep-alive message.
|
|
#[derive(SerialEncodable, SerialDecodable)]
|
|
pub struct PingMessage {
|
|
pub nonce: u32,
|
|
}
|
|
|
|
/// Inbound keep-alive message.
|
|
#[derive(SerialEncodable, SerialDecodable)]
|
|
pub struct PongMessage {
|
|
pub nonce: u32,
|
|
}
|
|
|
|
/// Requests address of outbound connection.
|
|
#[derive(SerialEncodable, SerialDecodable)]
|
|
pub struct GetAddrsMessage {}
|
|
|
|
/// Sends address information to inbound connection. Response to GetAddrs
|
|
/// message.
|
|
#[derive(SerialEncodable, SerialDecodable)]
|
|
pub struct AddrsMessage {
|
|
pub addrs: Vec<Url>,
|
|
}
|
|
|
|
/// Sends external address information to inbound connection.
|
|
#[derive(SerialEncodable, SerialDecodable)]
|
|
pub struct ExtAddrsMessage {
|
|
pub ext_addrs: Vec<Url>,
|
|
}
|
|
|
|
/// Requests version information of outbound connection.
|
|
#[derive(SerialEncodable, SerialDecodable)]
|
|
pub struct VersionMessage {
|
|
pub node_id: String,
|
|
}
|
|
|
|
/// Sends version information to inbound connection. Response to VersionMessage.
|
|
#[derive(SerialEncodable, SerialDecodable)]
|
|
pub struct VerackMessage {
|
|
// app version
|
|
pub app: String,
|
|
}
|
|
|
|
impl Message for PingMessage {
|
|
fn name() -> &'static str {
|
|
"ping"
|
|
}
|
|
}
|
|
|
|
impl Message for PongMessage {
|
|
fn name() -> &'static str {
|
|
"pong"
|
|
}
|
|
}
|
|
|
|
impl Message for GetAddrsMessage {
|
|
fn name() -> &'static str {
|
|
"getaddr"
|
|
}
|
|
}
|
|
|
|
impl Message for AddrsMessage {
|
|
fn name() -> &'static str {
|
|
"addr"
|
|
}
|
|
}
|
|
|
|
impl Message for ExtAddrsMessage {
|
|
fn name() -> &'static str {
|
|
"extaddr"
|
|
}
|
|
}
|
|
|
|
impl Message for VersionMessage {
|
|
fn name() -> &'static str {
|
|
"version"
|
|
}
|
|
}
|
|
|
|
impl Message for VerackMessage {
|
|
fn name() -> &'static str {
|
|
"verack"
|
|
}
|
|
}
|
|
|
|
/// Packets are the base type read from the network. Converted to messages and
|
|
/// passed to event loop.
|
|
pub struct Packet {
|
|
pub command: String,
|
|
pub payload: Vec<u8>,
|
|
}
|
|
|
|
/// Reads and decodes an inbound payload.
|
|
pub async fn read_packet<R: AsyncRead + Unpin + Sized>(stream: &mut R) -> Result<Packet> {
|
|
// Packets have a 4 byte header of magic digits
|
|
// This is used for network debugging
|
|
let mut magic = [0u8; 4];
|
|
debug!(target: "net::message", "reading magic...");
|
|
|
|
stream.read_exact(&mut magic).await?;
|
|
|
|
debug!(target: "net::message", "read magic {:?}", magic);
|
|
if magic != MAGIC_BYTES {
|
|
return Err(Error::MalformedPacket)
|
|
}
|
|
|
|
// The type of the message
|
|
let command_len = VarInt::decode_async(stream).await?.0 as usize;
|
|
let mut cmd = vec![0u8; command_len];
|
|
if command_len > 0 {
|
|
stream.read_exact(&mut cmd).await?;
|
|
}
|
|
let cmd = String::from_utf8(cmd)?;
|
|
debug!(target: "net::message", "read command: {}", cmd);
|
|
|
|
let payload_len = VarInt::decode_async(stream).await?.0 as usize;
|
|
|
|
// The message-dependent data (see message types)
|
|
let mut payload = vec![0u8; payload_len];
|
|
if payload_len > 0 {
|
|
stream.read_exact(&mut payload).await?;
|
|
}
|
|
debug!(target: "net::message", "read payload {} bytes", payload_len);
|
|
|
|
Ok(Packet { command: cmd, payload })
|
|
}
|
|
|
|
/// Sends an outbound packet by writing data to TCP stream.
|
|
pub async fn send_packet<W: AsyncWrite + Unpin + Sized>(
|
|
stream: &mut W,
|
|
packet: Packet,
|
|
) -> Result<()> {
|
|
debug!(target: "net::message", "sending magic...");
|
|
stream.write_all(&MAGIC_BYTES).await?;
|
|
debug!(target: "net::message", "sent magic...");
|
|
|
|
VarInt(packet.command.len() as u64).encode_async(stream).await?;
|
|
assert!(!packet.command.is_empty());
|
|
stream.write_all(packet.command.as_bytes()).await?;
|
|
debug!(target: "net::message", "sent command: {}", packet.command);
|
|
|
|
assert!(std::mem::size_of::<usize>() <= std::mem::size_of::<u64>());
|
|
VarInt(packet.payload.len() as u64).encode_async(stream).await?;
|
|
|
|
if !packet.payload.is_empty() {
|
|
stream.write_all(&packet.payload).await?;
|
|
}
|
|
debug!(target: "net::message", "sent payload {} bytes", packet.payload.len() as u64);
|
|
|
|
Ok(())
|
|
}
|