From 477d9e42f3cef255432b35f659ee29b131fd040b Mon Sep 17 00:00:00 2001 From: Boog900 <108027008+Boog900@users.noreply.github.com> Date: Mon, 17 Jul 2023 17:43:34 +0000 Subject: [PATCH] change monero-wire to use epee-encoding (#25) * change monero-wire to use epee-encoding * Add back unified `Message` enum * Run clippy & fmt * add back default val for top_version --- Cargo.toml | 8 +- net/levin/src/lib.rs | 8 +- net/monero-wire/Cargo.toml | 13 +- net/monero-wire/src/internal_macros.rs | 92 ---- net/monero-wire/src/lib.rs | 12 +- net/monero-wire/src/messages.rs | 489 ++++++++---------- net/monero-wire/src/messages/admin.rs | 159 +----- net/monero-wire/src/messages/common.rs | 272 +++------- .../src/messages/common/builders.rs | 67 +++ net/monero-wire/src/messages/protocol.rs | 158 +----- net/monero-wire/src/network_address.rs | 134 +---- .../src/network_address/builder.rs | 167 ++++++ net/monero-wire/src/utils.rs | 45 -- 13 files changed, 588 insertions(+), 1036 deletions(-) delete mode 100644 net/monero-wire/src/internal_macros.rs create mode 100644 net/monero-wire/src/messages/common/builders.rs create mode 100644 net/monero-wire/src/network_address/builder.rs delete mode 100644 net/monero-wire/src/utils.rs diff --git a/Cargo.toml b/Cargo.toml index d69c2e7..6fdd5c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,12 +2,12 @@ [workspace] members = [ - "common", - "cuprate", - "database", + # "common", + #"cuprate", + # "database", "net/levin", "net/monero-wire", - "p2p", + # "p2p", # "p2p/sync-states" ] diff --git a/net/levin/src/lib.rs b/net/levin/src/lib.rs index c1f8510..6bb8cb6 100644 --- a/net/levin/src/lib.rs +++ b/net/levin/src/lib.rs @@ -59,6 +59,9 @@ pub enum BucketError { /// Invalid Fragmented Message #[error("Levin fragmented message was invalid: {0}")] InvalidFragmentedMessage(&'static str), + /// Error decoding the body + #[error("Error decoding bucket body")] + BodyDecodingError(Box), /// I/O error #[error("I/O error: {0}")] IO(#[from] std::io::Error), @@ -73,7 +76,7 @@ pub struct Bucket { pub body: Vec, } -/// An enum representing if the message is a request or response +/// An enum representing if the message is a request, response or notification. #[derive(Debug, Eq, PartialEq)] pub enum MessageType { /// Request @@ -125,6 +128,7 @@ impl MessageType { } } +#[derive(Debug)] pub struct BucketBuilder { signature: Option, ty: Option, @@ -193,7 +197,7 @@ impl BucketBuilder { /// A levin body pub trait LevinBody: Sized { /// Decodes the message from the data in the header - fn decode_message(buf: &[u8], typ: MessageType, command: u32) -> Result; + fn decode_message(body: &[u8], typ: MessageType, command: u32) -> Result; /// Encodes the message fn encode(&self, builder: &mut BucketBuilder) -> Result<(), BucketError>; diff --git a/net/monero-wire/Cargo.toml b/net/monero-wire/Cargo.toml index 1d15b89..3d3aee5 100644 --- a/net/monero-wire/Cargo.toml +++ b/net/monero-wire/Cargo.toml @@ -8,14 +8,9 @@ repository = "https://github.com/SyntheticBird45/cuprate/tree/main/net/monero-wi [dependencies] -levin = {path="../levin"} -serde = {version = "1.0", features =["derive"]} -serde_with = "2.2.0" -sealed = "0.4" -epee-serde = {git="https://github.com/Cuprate/epee_serde.git"} -#monero = {git="https://github.com/Boog900/monero-rs.git", branch="db", features=["database"]} -byteorder = "1.4.3" -bytes = "1" -thiserror = "1.0.24" +levin-cuprate = {path="../levin"} +epee-encoding = { git = "https://github.com/boog900/epee-encoding"} + +[dev-dependencies] hex = "0.4.3" diff --git a/net/monero-wire/src/internal_macros.rs b/net/monero-wire/src/internal_macros.rs deleted file mode 100644 index c2e1e0b..0000000 --- a/net/monero-wire/src/internal_macros.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Rust Levin Library -// Written in 2023 by -// Cuprate Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// - -macro_rules! message { - ( - Admin, - Name: $name:ident, - ID: $id:expr, - Request: $req:ident { - EncodingError: $req_enc_err:path, - Encode: $req_enc:path, - Decode: $req_dec:path, - }, - Response: $res:ident { - EncodingError: $res_enc_err:path, - Encode: $res_enc:path, - Decode: $res_dec:path, - }, - ) => { - #[sealed::sealed] - impl crate::messages::NetworkMessage for $req { - type EncodingError = $req_enc_err; - fn decode(buf: &[u8]) -> Result { - $req_dec(buf) - } - fn encode(&self) -> Result, Self::EncodingError> { - $req_enc(self) - } - } - #[sealed::sealed] - impl crate::messages::NetworkMessage for $res { - type EncodingError = $res_enc_err; - fn decode(buf: &[u8]) -> Result { - $res_dec(buf) - } - fn encode(&self) -> Result, Self::EncodingError> { - $res_enc(self) - } - } - - pub struct $name; - - #[sealed::sealed] - impl crate::messages::AdminMessage for $name { - const ID: u32 = $id; - const NAME: &'static str = stringify!($name); - - type Request = $req; - type Response = $res; - } - }; - ( - Protocol, - Name: $name:ident { - EncodingError: $enc_err:path, - Encode: $enc:path, - Decode: $dec:path, - }, - ID: $id:expr, - ) => { - #[sealed::sealed] - impl crate::messages::NetworkMessage for $name { - type EncodingError = $enc_err; - fn decode(buf: &[u8]) -> Result { - $dec(buf) - } - fn encode(&self) -> Result, Self::EncodingError> { - $enc(self) - } - } - - #[sealed::sealed] - impl crate::messages::ProtocolMessage for $name { - const ID: u32 = $id; - const NAME: &'static str = stringify!($name); - - type Notification = Self; - } - }; -} diff --git a/net/monero-wire/src/lib.rs b/net/monero-wire/src/lib.rs index 05c20bc..4428ee1 100644 --- a/net/monero-wire/src/lib.rs +++ b/net/monero-wire/src/lib.rs @@ -16,7 +16,7 @@ //! # Monero Wire //! //! A crate defining Monero network messages and network addresses, -//! built on top of the levin crate. +//! built on top of the levin-cuprate crate. //! //! ## License //! @@ -29,13 +29,11 @@ #![deny(unused_mut)] //#![deny(missing_docs)] -#[macro_use] -mod internal_macros; pub mod messages; pub mod network_address; -mod utils; -pub use messages::{Message, P2pCommand}; pub use network_address::NetworkAddress; -// re-exports -pub use levin; + +pub use messages::*; + +pub type MoneroWireCodec = levin_cuprate::codec::LevinMessageCodec; diff --git a/net/monero-wire/src/messages.rs b/net/monero-wire/src/messages.rs index c278c61..14e8028 100644 --- a/net/monero-wire/src/messages.rs +++ b/net/monero-wire/src/messages.rs @@ -16,293 +16,234 @@ //! This module defines a Monero `Message` enum which contains //! every possible Monero network message (levin body) +use levin_cuprate::{BucketBuilder, BucketError, LevinBody, MessageType}; + pub mod admin; pub mod common; pub mod protocol; -pub use admin::{Handshake, Ping, SupportFlags, TimedSync}; -pub use common::{BasicNodeData, CoreSyncData, PeerID, PeerListEntryBase}; +pub use admin::{ + HandshakeRequest, HandshakeResponse, PingResponse, SupportFlagsResponse, TimedSyncRequest, + TimedSyncResponse, +}; +pub use common::{BasicNodeData, CoreSyncData, PeerListEntryBase}; pub use protocol::{ ChainRequest, ChainResponse, FluffyMissingTransactionsRequest, GetObjectsRequest, GetObjectsResponse, GetTxPoolCompliment, NewBlock, NewFluffyBlock, NewTransactions, }; -use levin::{BucketError, MessageType}; - -#[sealed::sealed] -pub trait NetworkMessage: Sized { - type EncodingError: std::fmt::Debug; - fn decode(buf: &[u8]) -> Result; - fn encode(&self) -> Result, Self::EncodingError>; +pub enum ProtocolMessage { + NewBlock(NewBlock), + NewFluffyBlock(NewFluffyBlock), + GetObjectsRequest(GetObjectsRequest), + GetObjectsResponse(GetObjectsResponse), + ChainRequest(ChainRequest), + ChainEntryResponse(ChainResponse), + NewTransactions(NewTransactions), + FluffyMissingTransactionsRequest(FluffyMissingTransactionsRequest), + GetTxPoolCompliment(GetTxPoolCompliment), } -#[sealed::sealed] -pub trait AdminMessage { - const ID: u32; - const NAME: &'static str; - type Request: NetworkMessage; - type Response: NetworkMessage; +impl ProtocolMessage { + fn decode(buf: &[u8], command: u32) -> Result { + Ok(match command { + 2001 => ProtocolMessage::NewBlock(epee_encoding::from_bytes(buf)?), + 2002 => ProtocolMessage::NewTransactions(epee_encoding::from_bytes(buf)?), + 2003 => ProtocolMessage::GetObjectsRequest(epee_encoding::from_bytes(buf)?), + 2004 => ProtocolMessage::GetObjectsResponse(epee_encoding::from_bytes(buf)?), + 2006 => ProtocolMessage::ChainRequest(epee_encoding::from_bytes(buf)?), + 2007 => ProtocolMessage::ChainEntryResponse(epee_encoding::from_bytes(buf)?), + 2008 => ProtocolMessage::NewFluffyBlock(epee_encoding::from_bytes(buf)?), + 2009 => { + ProtocolMessage::FluffyMissingTransactionsRequest(epee_encoding::from_bytes(buf)?) + } + 2010 => ProtocolMessage::GetTxPoolCompliment(epee_encoding::from_bytes(buf)?), + _ => { + return Err(epee_encoding::Error::Value( + "Failed to decode message, unknown command", + )) + } + }) + } + + fn build(&self, builder: &mut BucketBuilder) -> Result<(), epee_encoding::Error> { + match self { + ProtocolMessage::NewBlock(nb) => { + builder.set_command(2001); + builder.set_body(epee_encoding::to_bytes(nb)?); + } + ProtocolMessage::NewTransactions(nt) => { + builder.set_command(2002); + builder.set_body(epee_encoding::to_bytes(nt)?); + } + ProtocolMessage::GetObjectsRequest(gt) => { + builder.set_command(2003); + builder.set_body(epee_encoding::to_bytes(gt)?); + } + ProtocolMessage::GetObjectsResponse(ge) => { + builder.set_command(2004); + builder.set_body(epee_encoding::to_bytes(ge)?); + } + ProtocolMessage::ChainRequest(ct) => { + builder.set_command(2006); + builder.set_body(epee_encoding::to_bytes(ct)?); + } + ProtocolMessage::ChainEntryResponse(ce) => { + builder.set_command(2007); + builder.set_body(epee_encoding::to_bytes(ce)?); + } + ProtocolMessage::NewFluffyBlock(fb) => { + builder.set_command(2008); + builder.set_body(epee_encoding::to_bytes(fb)?); + } + ProtocolMessage::FluffyMissingTransactionsRequest(ft) => { + builder.set_command(2009); + builder.set_body(epee_encoding::to_bytes(ft)?); + } + ProtocolMessage::GetTxPoolCompliment(tp) => { + builder.set_command(2010); + builder.set_body(epee_encoding::to_bytes(tp)?); + } + } + Ok(()) + } } -#[sealed::sealed] -pub trait ProtocolMessage { - const ID: u32; - const NAME: &'static str; - type Notification: NetworkMessage; -} - -macro_rules! p2p_command { - ($($message:ident),+) => { - pub enum P2pCommand { - $($message,)+ - } - - pub struct P2pCommandFromU32Err; - impl TryFrom for P2pCommand { - type Error = P2pCommandFromU32Err; - fn try_from(value: u32) -> Result { - match value { - $($message::ID => Ok(P2pCommand::$message),)+ - _ => Err(P2pCommandFromU32Err) - } - } - } - impl From for u32 { - fn from(val: P2pCommand) -> Self { - match val { - $(P2pCommand::$message => $message::ID,)+ - } - } - } - }; -} - -macro_rules! levin_body { - ( - Admin: - $($admin_mes:ident),+ - Protocol: - $($protocol_mes:ident),+ - ) => { - - #[derive(Debug, Clone)] - pub enum MessageRequest { - $($admin_mes(<$admin_mes as AdminMessage>::Request),)+ - } - - $( - impl From<<$admin_mes as AdminMessage>::Request> for MessageRequest { - fn from(value: <$admin_mes as AdminMessage>::Request) -> MessageRequest { - MessageRequest::$admin_mes(value) - } - } - )+ - - impl MessageRequest { - pub fn id(&self) -> u32 { - match self { - $(MessageRequest::$admin_mes(_) => $admin_mes::ID,)+ - } - } - pub fn decode(buf: &[u8], command: u32) -> Result { - match command { - $($admin_mes::ID => Ok( - MessageRequest::$admin_mes(<$admin_mes as AdminMessage>::Request::decode(buf) - .map_err(|e| BucketError::FailedToDecodeBucketBody(e.to_string()))?)),)+ - _ => Err(BucketError::UnsupportedP2pCommand(command)) - } - } - - pub fn encode(&self) -> Result<(u32, Vec), BucketError> { - match self { - $(MessageRequest::$admin_mes(mes) => Ok(($admin_mes::ID, mes.encode() - .map_err(|e| BucketError::FailedToEncodeBucketBody(e.to_string()))?)),)+ - } - } - } - - #[derive(Debug, Clone)] - pub enum MessageResponse { - $($admin_mes(<$admin_mes as AdminMessage>::Response),)+ - } - - $( - impl From<<$admin_mes as AdminMessage>::Response> for MessageResponse { - fn from(value: <$admin_mes as AdminMessage>::Response) -> MessageResponse { - MessageResponse::$admin_mes(value) - } - } - )+ - - impl MessageResponse { - pub fn id(&self) -> u32 { - match self { - $(MessageResponse::$admin_mes(_) => $admin_mes::ID,)+ - } - } - - pub fn decode(buf: &[u8], command: u32) -> Result { - match command { - $($admin_mes::ID => Ok( - MessageResponse::$admin_mes(<$admin_mes as AdminMessage>::Response::decode(buf) - .map_err(|e| BucketError::FailedToDecodeBucketBody(e.to_string()))?)),)+ - _ => Err(BucketError::UnsupportedP2pCommand(command)) - } - } - - pub fn encode(&self) -> Result<(u32, Vec), BucketError> { - match self { - $(MessageResponse::$admin_mes(mes) => Ok(($admin_mes::ID, mes.encode() - .map_err(|e| BucketError::FailedToEncodeBucketBody(e.to_string()))?)),)+ - } - } - } - - #[derive(Debug, Clone)] - pub enum MessageNotification { - $($protocol_mes(<$protocol_mes as ProtocolMessage>::Notification),)+ - } - - $( - impl From<<$protocol_mes as ProtocolMessage>::Notification> for MessageNotification { - fn from(value: <$protocol_mes as ProtocolMessage>::Notification) -> MessageNotification { - MessageNotification::$protocol_mes(value) - } - } - )+ - - - impl MessageNotification { - pub fn id(&self) -> u32 { - match self { - $(MessageNotification::$protocol_mes(_) => $protocol_mes::ID,)+ - } - } - - - pub fn decode(buf: &[u8], command: u32) -> Result { - match command { - $($protocol_mes::ID => Ok( - MessageNotification::$protocol_mes(<$protocol_mes as ProtocolMessage>::Notification::decode(buf) - .map_err(|e| BucketError::FailedToDecodeBucketBody(e.to_string()))?)),)+ - _ => Err(BucketError::UnsupportedP2pCommand(command)) - } - } - - pub fn encode(&self) -> Result<(u32, Vec), BucketError> { - match self { - $(MessageNotification::$protocol_mes(mes) => Ok(($protocol_mes::ID, mes.encode() - .map_err(|e| BucketError::FailedToEncodeBucketBody(e.to_string()))?)),)+ - } - } - } - - #[derive(Debug, Clone)] - pub enum Message { - Request(MessageRequest), - Response(MessageResponse), - Notification(MessageNotification) - } - - impl From for Message { - fn from(value: MessageResponse) -> Message { - Message::Response(value) - } - } - - impl From for Message { - fn from(value: MessageRequest) -> Message { - Message::Request(value) - } - } - - impl From for Message { - fn from(value: MessageNotification) -> Message { - Message::Notification(value) - } - } - - impl Message { - pub fn id(&self) -> u32 { - match self { - Message::Request(req) => req.id(), - Message::Response(res) => res.id(), - Message::Notification(noti) => noti.id(), - } - } - pub fn is_request(&self) -> bool { - matches!(self, Self::Request(_)) - } - pub fn is_response(&self) -> bool { - matches!(self, Self::Response(_)) - } - pub fn is_notification(&self) -> bool { - matches!(self, Self::Notification(_)) - } - } - - impl levin::LevinBody for Message { - fn decode_message(buf: &[u8], typ: MessageType, command: u32) -> Result { - Ok(match typ { - MessageType::Response => Message::Response(MessageResponse::decode(buf, command)?), - MessageType::Request => Message::Request(MessageRequest::decode(buf, command)?), - MessageType::Notification => Message::Notification(MessageNotification::decode(buf, command)?), - }) - } - - fn encode(&self) -> Result<(i32, u32, MessageType, Vec), BucketError> { - match self { - Message::Response(mes) => { - let (command, bytes)= mes.encode()?; - Ok((1, command, MessageType::Response, bytes)) - }, - Message::Request(mes) => { - let (command, bytes)= mes.encode()?; - Ok((0, command, MessageType::Request, bytes)) - }, - Message::Notification(mes) => { - let (command, bytes)= mes.encode()?; - Ok((0, command, MessageType::Notification, bytes)) - }, - } - } - - - } - - }; -} - -p2p_command!( - Handshake, - TimedSync, +pub enum RequestMessage { + Handshake(HandshakeRequest), Ping, SupportFlags, - NewBlock, - NewTransactions, - GetObjectsRequest, - GetObjectsResponse, - ChainRequest, - ChainResponse, - NewFluffyBlock, - FluffyMissingTransactionsRequest, - GetTxPoolCompliment -); + TimedSync(TimedSyncRequest), +} -levin_body!( - Admin: - Handshake, - TimedSync, - Ping, - SupportFlags - Protocol: - NewBlock, - NewTransactions, - GetObjectsRequest, - GetObjectsResponse, - ChainRequest, - ChainResponse, - NewFluffyBlock, - FluffyMissingTransactionsRequest, - GetTxPoolCompliment -); +impl RequestMessage { + fn decode(buf: &[u8], command: u32) -> Result { + Ok(match command { + 1001 => RequestMessage::Handshake(epee_encoding::from_bytes(buf)?), + 1002 => RequestMessage::TimedSync(epee_encoding::from_bytes(buf)?), + 1003 => RequestMessage::Ping, + 1007 => RequestMessage::SupportFlags, + _ => { + return Err(epee_encoding::Error::Value( + "Failed to decode message, unknown command", + )) + } + }) + } + + fn build(&self, builder: &mut BucketBuilder) -> Result<(), epee_encoding::Error> { + match self { + RequestMessage::Handshake(hs) => { + builder.set_command(1001); + builder.set_body(epee_encoding::to_bytes(hs)?); + } + RequestMessage::TimedSync(ts) => { + builder.set_command(1002); + builder.set_body(epee_encoding::to_bytes(ts)?); + } + RequestMessage::Ping => { + builder.set_command(1003); + builder.set_body(Vec::new()); + } + RequestMessage::SupportFlags => { + builder.set_command(1007); + builder.set_body(Vec::new()); + } + } + Ok(()) + } +} + +pub enum ResponseMessage { + Handshake(HandshakeResponse), + Ping(PingResponse), + SupportFlags(SupportFlagsResponse), + TimedSync(TimedSyncResponse), +} + +impl ResponseMessage { + fn decode(buf: &[u8], command: u32) -> Result { + Ok(match command { + 1001 => ResponseMessage::Handshake(epee_encoding::from_bytes(buf)?), + 1002 => ResponseMessage::TimedSync(epee_encoding::from_bytes(buf)?), + 1003 => ResponseMessage::Ping(epee_encoding::from_bytes(buf)?), + 1007 => ResponseMessage::SupportFlags(epee_encoding::from_bytes(buf)?), + _ => { + return Err(epee_encoding::Error::Value( + "Failed to decode message, unknown command", + )) + } + }) + } + + fn build(&self, builder: &mut BucketBuilder) -> Result<(), epee_encoding::Error> { + match self { + ResponseMessage::Handshake(hs) => { + builder.set_command(1001); + builder.set_body(epee_encoding::to_bytes(hs)?); + } + ResponseMessage::TimedSync(ts) => { + builder.set_command(1002); + builder.set_body(epee_encoding::to_bytes(ts)?); + } + ResponseMessage::Ping(pg) => { + builder.set_command(1003); + builder.set_body(epee_encoding::to_bytes(pg)?); + } + ResponseMessage::SupportFlags(sf) => { + builder.set_command(1007); + builder.set_body(epee_encoding::to_bytes(sf)?); + } + } + Ok(()) + } +} + +pub enum Message { + Request(RequestMessage), + Response(ResponseMessage), + Protocol(ProtocolMessage), +} + +impl LevinBody for Message { + fn decode_message(body: &[u8], typ: MessageType, command: u32) -> Result { + Ok(match typ { + MessageType::Request => Message::Request( + RequestMessage::decode(body, command) + .map_err(|e| BucketError::BodyDecodingError(Box::new(e)))?, + ), + MessageType::Response => Message::Response( + ResponseMessage::decode(body, command) + .map_err(|e| BucketError::BodyDecodingError(Box::new(e)))?, + ), + MessageType::Notification => Message::Protocol( + ProtocolMessage::decode(body, command) + .map_err(|e| BucketError::BodyDecodingError(Box::new(e)))?, + ), + }) + } + + fn encode(&self, builder: &mut BucketBuilder) -> Result<(), BucketError> { + match self { + Message::Protocol(pro) => { + builder.set_message_type(MessageType::Notification); + builder.set_return_code(0); + pro.build(builder) + .map_err(|e| BucketError::BodyDecodingError(Box::new(e)))?; + } + Message::Request(req) => { + builder.set_message_type(MessageType::Request); + builder.set_return_code(0); + req.build(builder) + .map_err(|e| BucketError::BodyDecodingError(Box::new(e)))?; + } + Message::Response(res) => { + builder.set_message_type(MessageType::Response); + builder.set_return_code(1); + res.build(builder) + .map_err(|e| BucketError::BodyDecodingError(Box::new(e)))?; + } + } + Ok(()) + } +} diff --git a/net/monero-wire/src/messages/admin.rs b/net/monero-wire/src/messages/admin.rs index b8888f1..d60040a 100644 --- a/net/monero-wire/src/messages/admin.rs +++ b/net/monero-wire/src/messages/admin.rs @@ -18,36 +18,12 @@ //! Admin message requests must be responded to in order unlike //! protocol messages. -use std::fmt::Display; +use epee_encoding::EpeeObject; -use serde::{Deserialize, Serialize}; - -use super::{ - common::{BasicNodeData, CoreSyncData, PeerListEntryBase, PeerSupportFlags}, - PeerID, -}; - -const P2P_ADMIN_BASE: u32 = 1000; - -#[derive(Debug)] -pub struct SillyEncodingError; - -impl Display for SillyEncodingError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("Literally impossible to get this error") - } -} - -fn silly_encode(_: &T) -> Result, SillyEncodingError> { - Ok(vec![]) -} - -fn silly_decode(_: &[u8]) -> Result { - Ok(T::default()) -} +use super::common::{BasicNodeData, CoreSyncData, PeerListEntryBase, PeerSupportFlags}; /// A Handshake Request -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct HandshakeRequest { /// Basic Node Data pub node_data: BasicNodeData, @@ -55,47 +31,27 @@ pub struct HandshakeRequest { pub payload_data: CoreSyncData, } -fn empty_vec() -> Vec { - vec![] -} - /// A Handshake Response -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct HandshakeResponse { /// Basic Node Data pub node_data: BasicNodeData, /// Core Sync Data pub payload_data: CoreSyncData, /// PeerList - #[serde(default = "empty_vec")] + #[epee_default(Vec::new())] pub local_peerlist_new: Vec, } -message!( - Admin, - Name: Handshake, - ID: P2P_ADMIN_BASE + 1, - Request: HandshakeRequest { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, - Response: HandshakeResponse { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, -); - /// A TimedSync Request -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct TimedSyncRequest { /// Core Sync Data pub payload_data: CoreSyncData, } /// A TimedSync Response -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct TimedSyncResponse { /// Core Sync Data pub payload_data: CoreSyncData, @@ -103,86 +59,31 @@ pub struct TimedSyncResponse { pub local_peerlist_new: Vec, } -message!( - Admin, - Name: TimedSync, - ID: P2P_ADMIN_BASE + 2, - Request: TimedSyncRequest { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, - Response: TimedSyncResponse { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, -); - /// The status field of an okay ping response pub const PING_OK_RESPONSE_STATUS_TEXT: &str = "OK"; -/// A Ping Request -#[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct PingRequest; - /// A Ping Response -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct PingResponse { /// Status: should be `PING_OK_RESPONSE_STATUS_TEXT` pub status: String, /// Peer ID - pub peer_id: PeerID, + pub peer_id: u64, } -message!( - Admin, - Name: Ping, - ID: P2P_ADMIN_BASE + 3, - Request: PingRequest { - EncodingError: SillyEncodingError, - Encode: silly_encode, - Decode: silly_decode, - }, - Response: PingResponse { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, -); - -/// A Support Flags Request -#[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct SupportFlagsRequest; - /// A Support Flags Response -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct SupportFlagsResponse { /// Support Flags + #[epee_try_from_into(u32)] pub support_flags: PeerSupportFlags, } -message!( - Admin, - Name: SupportFlags, - ID: P2P_ADMIN_BASE + 7, - Request: SupportFlagsRequest { - EncodingError: SillyEncodingError, - Encode: silly_encode, - Decode: silly_decode, - }, - Response: SupportFlagsResponse { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, -); - #[cfg(test)] mod tests { use super::{BasicNodeData, CoreSyncData, HandshakeRequest, HandshakeResponse}; - use crate::messages::common::{PeerID, PeerSupportFlags}; + use crate::messages::common::PeerSupportFlags; #[test] fn serde_handshake_req() { @@ -200,13 +101,13 @@ mod tests { 186, 15, 178, 70, 173, 170, 187, 31, 70, 50, 227, 11, 116, 111, 112, 95, 118, 101, 114, 115, 105, 111, 110, 8, 1, ]; - let handshake: HandshakeRequest = epee_serde::from_bytes(bytes).unwrap(); + let handshake: HandshakeRequest = epee_encoding::from_bytes(&bytes).unwrap(); let basic_node_data = BasicNodeData { my_port: 0, network_id: [ 18, 48, 241, 113, 97, 4, 65, 97, 23, 49, 0, 130, 22, 161, 161, 16, ], - peer_id: PeerID(9671405426614699871), + peer_id: 9671405426614699871, support_flags: PeerSupportFlags::from(1_u32), rpc_port: 0, rpc_credits_per_hash: 0, @@ -217,20 +118,18 @@ mod tests { cumulative_difficulty_top64: 0, current_height: 0, pruning_seed: 0, - top_id: hex::decode( - "0x418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3", - ) - .unwrap() - .try_into() - .unwrap(), + top_id: hex::decode("418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3") + .unwrap() + .try_into() + .unwrap(), top_version: 1, }; assert_eq!(basic_node_data, handshake.node_data); assert_eq!(core_sync_data, handshake.payload_data); - let encoded_bytes = epee_serde::to_bytes(&handshake).unwrap(); - let handshake_2: HandshakeRequest = epee_serde::from_bytes(encoded_bytes).unwrap(); + let encoded_bytes = epee_encoding::to_bytes(&handshake).unwrap(); + let handshake_2: HandshakeRequest = epee_encoding::from_bytes(&encoded_bytes).unwrap(); assert_eq!(handshake, handshake_2); } @@ -1006,14 +905,14 @@ mod tests { 181, 216, 193, 135, 23, 186, 168, 207, 119, 86, 235, 11, 116, 111, 112, 95, 118, 101, 114, 115, 105, 111, 110, 8, 16, ]; - let handshake: HandshakeResponse = epee_serde::from_bytes(bytes).unwrap(); + let handshake: HandshakeResponse = epee_encoding::from_bytes(&bytes).unwrap(); let basic_node_data = BasicNodeData { my_port: 18080, network_id: [ 18, 48, 241, 113, 97, 4, 65, 97, 23, 49, 0, 130, 22, 161, 161, 16, ], - peer_id: PeerID(6037804360359455404), + peer_id: 6037804360359455404, support_flags: PeerSupportFlags::from(1_u32), rpc_port: 18089, rpc_credits_per_hash: 0, @@ -1024,12 +923,10 @@ mod tests { cumulative_difficulty_top64: 0, current_height: 2775167, pruning_seed: 386, - top_id: hex::decode( - "0x40780072dae9123108599a9f6585f2474d03f7b6dbb5d8c18717baa8cf7756eb", - ) - .unwrap() - .try_into() - .unwrap(), + top_id: hex::decode("40780072dae9123108599a9f6585f2474d03f7b6dbb5d8c18717baa8cf7756eb") + .unwrap() + .try_into() + .unwrap(), top_version: 16, }; @@ -1037,8 +934,8 @@ mod tests { assert_eq!(core_sync_data, handshake.payload_data); assert_eq!(250, handshake.local_peerlist_new.len()); - let encoded_bytes = epee_serde::to_bytes(&handshake).unwrap(); - let handshake_2: HandshakeResponse = epee_serde::from_bytes(encoded_bytes).unwrap(); + let encoded_bytes = epee_encoding::to_bytes(&handshake).unwrap(); + let handshake_2: HandshakeResponse = epee_encoding::from_bytes(&encoded_bytes).unwrap(); assert_eq!(handshake, handshake_2); } diff --git a/net/monero-wire/src/messages/common.rs b/net/monero-wire/src/messages/common.rs index 8a20475..1775091 100644 --- a/net/monero-wire/src/messages/common.rs +++ b/net/monero-wire/src/messages/common.rs @@ -15,44 +15,14 @@ //! Common types that are used across multiple messages. // +use epee_encoding::EpeeObject; -use epee_serde::Value; -use serde::de; -use serde::ser::{SerializeSeq, SerializeStruct}; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; -use serde_with::TryFromInto; - -use crate::utils; use crate::NetworkAddress; -#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] -#[serde(transparent)] -pub struct PeerSupportFlags(u32); // had to name it this to avoid conflict +mod builders; -impl PeerSupportFlags { - const FLUFFY_BLOCKS: u32 = 0b0000_0001; - /// checks if `self` has all the flags that `other` has - pub fn contains(&self, other: &PeerSupportFlags) -> bool { - self.0 & other.0 == other.0 - } - pub fn supports_fluffy_blocks(&self) -> bool { - self.0 & Self::FLUFFY_BLOCKS == Self::FLUFFY_BLOCKS - } - pub fn get_support_flag_fluffy_blocks() -> Self { - PeerSupportFlags(Self::FLUFFY_BLOCKS) - } - - pub fn is_empty(&self) -> bool { - self.0 == 0 - } -} - -impl From for PeerSupportFlags { - fn from(value: u8) -> Self { - PeerSupportFlags(value as u32) - } -} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PeerSupportFlags(u32); impl From for PeerSupportFlags { fn from(value: u32) -> Self { @@ -60,56 +30,83 @@ impl From for PeerSupportFlags { } } -/// A PeerID, different from a `NetworkAddress` -#[derive(Debug, Clone, Default, Copy, Deserialize, Serialize, PartialEq, Eq)] -#[serde(transparent)] -pub struct PeerID(pub u64); +impl From for u32 { + fn from(value: PeerSupportFlags) -> Self { + value.0 + } +} + +/* +impl PeerSupportFlags { + const FLUFFY_BLOCKS: u32 = 0b0000_0001; + /// checks if `self` has all the flags that `other` has + pub fn contains(&self, other: &PeerSupportFlags) -> bool { + self.0. & other.0 == other.0 + } + pub fn supports_fluffy_blocks(&self) -> bool { + self.0 & Self::FLUFFY_BLOCKS == Self::FLUFFY_BLOCKS + } + pub fn get_support_flag_fluffy_blocks() -> Self { + PeerSupportFlags { + support_flags: Self::FLUFFY_BLOCKS, + } + } + + pub fn is_empty(&self) -> bool { + self.0 == 0 + } +} +*/ +impl From for PeerSupportFlags { + fn from(value: u8) -> Self { + PeerSupportFlags(value.into()) + } +} /// Basic Node Data, information on the connected peer -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct BasicNodeData { /// Port pub my_port: u32, /// The Network Id pub network_id: [u8; 16], /// Peer ID - pub peer_id: PeerID, + pub peer_id: u64, /// The Peers Support Flags /// (If this is not in the message the default is 0) - #[serde(default = "utils::zero_val")] + #[epee_try_from_into(u32)] + #[epee_default(0_u32)] pub support_flags: PeerSupportFlags, /// RPC Port /// (If this is not in the message the default is 0) - #[serde(default = "utils::zero_val")] + #[epee_default(0)] pub rpc_port: u16, /// RPC Credits Per Hash /// (If this is not in the message the default is 0) - #[serde(default = "utils::zero_val")] + #[epee_default(0)] pub rpc_credits_per_hash: u32, } /// Core Sync Data, information on the sync state of a peer -#[serde_as] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct CoreSyncData { /// Cumulative Difficulty Low /// The lower 64 bits of the 128 bit cumulative difficulty pub cumulative_difficulty: u64, /// Cumulative Difficulty High /// The upper 64 bits of the 128 bit cumulative difficulty - #[serde(default = "utils::zero_val")] + #[epee_default(0)] pub cumulative_difficulty_top64: u64, /// Current Height of the peer pub current_height: u64, /// Pruning Seed of the peer /// (If this is not in the message the default is 0) - #[serde(default = "utils::zero_val")] + #[epee_default(0)] pub pruning_seed: u32, /// Hash of the top block - #[serde_as(as = "TryFromInto<[u8; 32]>")] pub top_id: [u8; 32], /// Version of the top block - #[serde(default = "utils::zero_val")] + #[epee_default(0)] pub top_version: u8, } @@ -134,31 +131,31 @@ impl CoreSyncData { } /// Returns the 128 bit cumulative difficulty of the peers blockchain pub fn cumulative_difficulty(&self) -> u128 { - let mut ret: u128 = self.cumulative_difficulty_top64 as u128; + let mut ret: u128 = self.cumulative_difficulty_top64.into(); ret <<= 64; - ret | self.cumulative_difficulty as u128 + ret | (Into::::into(self.cumulative_difficulty)) } } /// PeerListEntryBase, information kept on a peer which will be entered /// in a peer list/store. -#[derive(Clone, Copy, Default, Deserialize, Serialize, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, EpeeObject, Debug, Eq, PartialEq)] pub struct PeerListEntryBase { /// The Peer Address pub adr: NetworkAddress, /// The Peer ID - pub id: PeerID, + pub id: u64, /// The last Time The Peer Was Seen - #[serde(default = "utils::zero_val")] + #[epee_default(0)] pub last_seen: i64, /// The Pruning Seed - #[serde(default = "utils::zero_val")] + #[epee_default(0)] pub pruning_seed: u32, /// The RPC port - #[serde(default = "utils::zero_val")] + #[epee_default(0)] pub rpc_port: u16, /// The RPC credits per hash - #[serde(default = "utils::zero_val")] + #[epee_default(0)] pub rpc_credits_per_hash: u32, } @@ -170,7 +167,7 @@ impl std::hash::Hash for PeerListEntryBase { } /// A pruned tx with the hash of the missing prunable data -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, EpeeObject, PartialEq, Eq)] pub struct PrunedTxBlobEntry { /// The Tx pub tx: Vec, @@ -178,39 +175,6 @@ pub struct PrunedTxBlobEntry { pub prunable_hash: [u8; 32], } -impl PrunedTxBlobEntry { - fn from_epee_value(mut value: Value) -> Result { - let tx = utils::get_internal_val_from_map(&mut value, "blob", Value::get_bytes, "Vec")?; - - let prunable_hash = utils::get_internal_val_from_map( - &mut value, - "prunable_hash", - Value::get_bytes, - "Vec", - )?; - let prunable_hash_len = prunable_hash.len(); - - Ok(PrunedTxBlobEntry { - tx, - prunable_hash: prunable_hash - .try_into() - .map_err(|_| E::invalid_length(prunable_hash_len, &"a 16-byte array"))?, - }) - } -} - -impl Serialize for PrunedTxBlobEntry { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut state = serializer.serialize_struct("", 2)?; - state.serialize_field("blob", &self.tx)?; - state.serialize_field("prunable_hash", &self.prunable_hash)?; - state.end() - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum TransactionBlobs { Pruned(Vec), @@ -228,138 +192,22 @@ impl TransactionBlobs { pub fn is_empty(&self) -> bool { self.len() == 0 } - - fn from_epee_value(value: Value, pruned: bool) -> Result { - let txs = utils::get_internal_val(value, Value::get_seq, "A sequence")?; - if pruned { - let mut decoded_txs = Vec::with_capacity(txs.len()); - for tx in txs { - decoded_txs.push(PrunedTxBlobEntry::from_epee_value(tx)?); - } - Ok(TransactionBlobs::Pruned(decoded_txs)) - } else { - let mut decoded_txs = Vec::with_capacity(txs.len()); - for tx in txs { - decoded_txs.push(utils::get_internal_val(tx, Value::get_bytes, "Vec")?); - } - Ok(TransactionBlobs::Normal(decoded_txs)) - } - } -} - -impl Serialize for TransactionBlobs { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - match self { - TransactionBlobs::Pruned(txs) => { - let mut seq = serializer.serialize_seq(Some(txs.len()))?; - - for tx in txs { - seq.serialize_element(tx)?; - } - - seq.end() - } - TransactionBlobs::Normal(txs) => { - let mut seq = serializer.serialize_seq(Some(txs.len()))?; - - for tx in txs { - seq.serialize_element(tx)?; - } - - seq.end() - } - } - } } /// A Block that can contain transactions -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, EpeeObject, PartialEq, Eq)] pub struct BlockCompleteEntry { /// True if tx data is pruned + #[epee_default(false)] pub pruned: bool, /// The Block pub block: Vec, /// The Block Weight/Size + #[epee_default(0)] pub block_weight: u64, /// The blocks txs - pub txs: TransactionBlobs, -} - -impl<'de> Deserialize<'de> for BlockCompleteEntry { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let mut value = Value::deserialize(deserializer)?; - let mut pruned = false; - if let Some(val) = value.get_and_remove("pruned") { - pruned = utils::get_internal_val(val, Value::get_bool, "bool")?; - } - - let block = - utils::get_internal_val_from_map(&mut value, "block", Value::get_bytes, "Vec")?; - - let mut block_weight = 0; - - let txs_value = value.get_and_remove("txs"); - - let mut txs = TransactionBlobs::Normal(vec![]); - - if let Some(txs_value) = txs_value { - txs = TransactionBlobs::from_epee_value(txs_value, true)?; - } - - if pruned { - block_weight = utils::get_internal_val_from_map( - &mut value, - "block_weight", - Value::get_u64, - "u64", - )?; - } - - Ok(BlockCompleteEntry { - pruned, - block, - block_weight, - txs, - }) - } -} - -impl Serialize for BlockCompleteEntry { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut len = 1; - if !self.txs.is_empty() { - len += 1; - } - if self.pruned { - // one field to store the value of `pruned` - // another to sore the block weight - len += 2; - } - - let mut state = serializer.serialize_struct("", len)?; - - state.serialize_field("block", &self.block)?; - - if self.pruned { - state.serialize_field("pruned", &true)?; - state.serialize_field("block_weight", &self.block_weight)?; - } - - if !self.txs.is_empty() { - state.serialize_field("txs", &self.txs)?; - } - - state.end() - } + #[epee_default(None)] + pub txs: Option, } #[cfg(test)] diff --git a/net/monero-wire/src/messages/common/builders.rs b/net/monero-wire/src/messages/common/builders.rs new file mode 100644 index 0000000..d94e4bd --- /dev/null +++ b/net/monero-wire/src/messages/common/builders.rs @@ -0,0 +1,67 @@ +use epee_encoding::{ + error::Error, + io::{Read, Write}, + marker::InnerMarker, + write_field, EpeeObject, EpeeObjectBuilder, EpeeValue, +}; + +use super::{PrunedTxBlobEntry, TransactionBlobs}; + +impl EpeeObject for TransactionBlobs { + type Builder = TransactionBlobsBuilder; + fn number_of_fields(&self) -> u64 { + 1 + } + fn write_fields(&self, w: &mut W) -> epee_encoding::error::Result<()> { + match self { + TransactionBlobs::Pruned(txs) => write_field(txs, "txs", w), + TransactionBlobs::Normal(txs) => write_field(txs, "txs", w), + } + } +} + +#[derive(Default)] +pub enum TransactionBlobsBuilder { + #[default] + Init, + Pruned(Vec), + Normal(Vec>), +} + +impl EpeeObjectBuilder for TransactionBlobsBuilder { + fn add_field(&mut self, name: &str, r: &mut R) -> epee_encoding::error::Result { + if name != "txs" { + return Ok(false); + } + + let marker = epee_encoding::read_marker(r)?; + + if !marker.is_seq { + return Err(Error::Format("Expected a sequence but got a single value")); + } + + match marker.inner_marker { + InnerMarker::String => { + let state = TransactionBlobsBuilder::Normal(Vec::>::read(r, &marker)?); + let _ = std::mem::replace(self, state); + } + InnerMarker::Object => { + let state = + TransactionBlobsBuilder::Pruned(Vec::::read(r, &marker)?); + let _ = std::mem::replace(self, state); + } + + _ => return Err(Error::Format("Unexpected marker")), + } + + Ok(true) + } + + fn finish(self) -> epee_encoding::error::Result { + match self { + TransactionBlobsBuilder::Init => Err(Error::Format("Required field was not in data")), + TransactionBlobsBuilder::Normal(txs) => Ok(TransactionBlobs::Normal(txs)), + TransactionBlobsBuilder::Pruned(txs) => Ok(TransactionBlobs::Pruned(txs)), + } + } +} diff --git a/net/monero-wire/src/messages/protocol.rs b/net/monero-wire/src/messages/protocol.rs index 0ff9ca9..14c5961 100644 --- a/net/monero-wire/src/messages/protocol.rs +++ b/net/monero-wire/src/messages/protocol.rs @@ -18,18 +18,12 @@ //! Protocol message requests don't have to be responded to in order unlike //! admin messages. -use serde::Deserialize; -use serde::Serialize; -use serde_with::serde_as; -use serde_with::Bytes; +use epee_encoding::EpeeObject; use super::common::BlockCompleteEntry; -use crate::utils::{default_false, default_true}; - -const P2P_PROTOCOL_BASE: u32 = 2000; /// A block that SHOULD have transactions -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct NewBlock { /// Block with txs pub b: BlockCompleteEntry, @@ -37,71 +31,31 @@ pub struct NewBlock { pub current_blockchain_height: u64, } -message!( - Protocol, - Name: NewBlock { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, - ID: P2P_PROTOCOL_BASE + 1, -); - -/// A Tx Pool transaction blob -#[serde_as] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] -#[serde(transparent)] -pub struct TxBlob(#[serde_as(as = "Bytes")] pub Vec); - /// New Tx Pool Transactions -#[serde_as] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct NewTransactions { /// Tx Blobs - pub txs: Vec, + pub txs: Vec>, /// Dandelionpp true if fluff - backwards compatible mode is fluff - #[serde(default = "default_true")] + #[epee_default(true)] pub dandelionpp_fluff: bool, /// Padding - #[serde(rename = "_")] - #[serde_as(as = "Bytes")] + #[epee_alt_name("_")] pub padding: Vec, } -message!( - Protocol, - Name: NewTransactions { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, - ID: P2P_PROTOCOL_BASE + 2, -); - /// A Request For Blocks -#[serde_as] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct GetObjectsRequest { - /// Blocks + /// Block hashes we want pub blocks: Vec<[u8; 32]>, /// Pruned - #[serde(default = "default_false")] + #[epee_default(false)] pub pruned: bool, } -message!( - Protocol, - Name: GetObjectsRequest { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, - ID: P2P_PROTOCOL_BASE + 3, -); - /// A Blocks Response -#[serde_as] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct GetObjectsResponse { /// Blocks pub blocks: Vec, @@ -111,40 +65,18 @@ pub struct GetObjectsResponse { pub current_blockchain_height: u64, } -message!( - Protocol, - Name: GetObjectsResponse { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, - ID: P2P_PROTOCOL_BASE + 4, -); - /// A Chain Request -#[serde_as] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct ChainRequest { /// Block IDs pub block_ids: Vec<[u8; 32]>, /// Prune - #[serde(default = "default_false")] + #[epee_default(false)] pub prune: bool, } -message!( - Protocol, - Name: ChainRequest { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, - ID: P2P_PROTOCOL_BASE + 6, -); - /// A Chain Response -#[serde_as] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct ChainResponse { /// Start Height pub start_height: u64, @@ -159,7 +91,6 @@ pub struct ChainResponse { /// Block Weights pub m_block_weights: Vec, /// The first Block in the response - #[serde_as(as = "Bytes")] pub first_block: Vec, } @@ -191,18 +122,8 @@ impl ChainResponse { } } -message!( - Protocol, - Name: ChainResponse { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, - ID: P2P_PROTOCOL_BASE + 7, -); - /// A Block that doesn't have transactions unless requested -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct NewFluffyBlock { /// Block which might have transactions pub b: BlockCompleteEntry, @@ -210,19 +131,8 @@ pub struct NewFluffyBlock { pub current_blockchain_height: u64, } -message!( - Protocol, - Name: NewFluffyBlock { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, - ID: P2P_PROTOCOL_BASE + 8, -); - /// A request for Txs we are missing from our TxPool -#[serde_as] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct FluffyMissingTransactionsRequest { /// The Block we are missing the Txs in pub block_hash: [u8; 32], @@ -232,34 +142,13 @@ pub struct FluffyMissingTransactionsRequest { pub missing_tx_indices: Vec, } -message!( - Protocol, - Name: FluffyMissingTransactionsRequest { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, - ID: P2P_PROTOCOL_BASE + 9, -); - /// TxPoolCompliment -#[serde_as] -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] pub struct GetTxPoolCompliment { /// Tx Hashes pub hashes: Vec<[u8; 32]>, } -message!( - Protocol, - Name: GetTxPoolCompliment { - EncodingError: epee_serde::Error, - Encode: epee_serde::to_bytes, - Decode: epee_serde::from_bytes, - }, - ID: P2P_PROTOCOL_BASE + 10, -); - #[cfg(test)] mod tests { @@ -778,16 +667,17 @@ mod tests { let now = std::time::Instant::now(); for _ in 0..1000 { - let _new_transactions: NewTransactions = epee_serde::from_bytes(bytes).unwrap(); + let _new_transactions: NewTransactions = epee_encoding::from_bytes(&bytes).unwrap(); } println!("in: {}ms", now.elapsed().as_millis()); - let new_transactions: NewTransactions = epee_serde::from_bytes(bytes).unwrap(); + let new_transactions: NewTransactions = epee_encoding::from_bytes(&bytes).unwrap(); assert_eq!(4, new_transactions.txs.len()); - let encoded_bytes = epee_serde::to_bytes(&new_transactions).unwrap(); - let new_transactions_2: NewTransactions = epee_serde::from_bytes(encoded_bytes).unwrap(); + let encoded_bytes = epee_encoding::to_bytes(&new_transactions).unwrap(); + let new_transactions_2: NewTransactions = + epee_encoding::from_bytes(&encoded_bytes).unwrap(); assert_eq!(new_transactions, new_transactions_2); } @@ -1133,10 +1023,10 @@ mod tests { 101, 110, 116, 95, 98, 108, 111, 99, 107, 99, 104, 97, 105, 110, 95, 104, 101, 105, 103, 104, 116, 5, 209, 45, 42, 0, 0, 0, 0, 0, ]; - let fluffy_block: NewFluffyBlock = epee_serde::from_bytes(bytes).unwrap(); + let fluffy_block: NewFluffyBlock = epee_encoding::from_bytes(&bytes).unwrap(); - let encoded_bytes = epee_serde::to_bytes(&fluffy_block).unwrap(); - let fluffy_block_2: NewFluffyBlock = epee_serde::from_bytes(encoded_bytes).unwrap(); + let encoded_bytes = epee_encoding::to_bytes(&fluffy_block).unwrap(); + let fluffy_block_2: NewFluffyBlock = epee_encoding::from_bytes(&encoded_bytes).unwrap(); assert_eq!(fluffy_block, fluffy_block_2); } diff --git a/net/monero-wire/src/network_address.rs b/net/monero-wire/src/network_address.rs index 5102b96..69365b9 100644 --- a/net/monero-wire/src/network_address.rs +++ b/net/monero-wire/src/network_address.rs @@ -17,12 +17,10 @@ //! Monero network. Core Monero has 4 main addresses: IPv4, IPv6, Tor, //! I2p. Currently this module only has IPv(4/6). //! +use std::net::{SocketAddrV4, SocketAddrV6}; use std::{hash::Hash, net}; -use epee_serde::Value; -use serde::{de, ser::SerializeStruct, Deserialize, Serialize}; - -use super::utils; +mod builder; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum NetZone { @@ -31,80 +29,14 @@ pub enum NetZone { I2p, } -/// An IPv4 address with a port -#[derive(Clone, Copy, Serialize, Debug, Default, PartialEq, Eq, Hash)] -pub struct IPv4Address { - /// IP address - pub m_ip: u32, - /// Port - pub m_port: u16, -} - -impl From for IPv4Address { - fn from(value: net::SocketAddrV4) -> Self { - IPv4Address { - m_ip: u32::from_le_bytes(value.ip().octets()), - m_port: value.port(), - } - } -} - -impl IPv4Address { - fn from_value(mut value: Value) -> Result { - let m_ip = utils::get_internal_val_from_map(&mut value, "m_ip", Value::get_u32, "u32")?; - - let m_port = utils::get_internal_val_from_map(&mut value, "m_port", Value::get_u16, "u16")?; - - Ok(IPv4Address { - m_ip: m_ip, - m_port: m_port, - }) - } -} - -/// An IPv6 address with a port -#[derive(Clone, Copy, Serialize, Debug, Default, PartialEq, Eq, Hash)] -pub struct IPv6Address { - /// Address - pub addr: [u8; 16], - /// Port - pub m_port: u16, -} - -impl From for IPv6Address { - fn from(value: net::SocketAddrV6) -> Self { - IPv6Address { - addr: value.ip().octets(), - m_port: value.port(), - } - } -} - -impl IPv6Address { - fn from_value(mut value: Value) -> Result { - let addr = - utils::get_internal_val_from_map(&mut value, "addr", Value::get_bytes, "Vec")?; - let addr_len = addr.len(); - - let m_port = utils::get_internal_val_from_map(&mut value, "m_port", Value::get_u16, "u16")?; - - Ok(IPv6Address { - addr: addr - .try_into() - .map_err(|_| E::invalid_length(addr_len, &"a 16-byte array"))?, - m_port, - }) - } -} - /// A network address which can be encoded into the format required /// to send to other Monero peers. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum NetworkAddress { /// IPv4 - IPv4(IPv4Address), + IPv4(SocketAddrV4), /// IPv6 - IPv6(IPv6Address), + IPv6(SocketAddrV6), } impl NetworkAddress { @@ -126,27 +58,21 @@ impl NetworkAddress { pub fn port(&self) -> u16 { match self { - NetworkAddress::IPv4(ip) => ip.m_port, - NetworkAddress::IPv6(ip) => ip.m_port, + NetworkAddress::IPv4(ip) => ip.port(), + NetworkAddress::IPv6(ip) => ip.port(), } } } -impl Default for NetworkAddress { - fn default() -> Self { - Self::IPv4(IPv4Address::default()) - } -} - impl From for NetworkAddress { fn from(value: net::SocketAddrV4) -> Self { - NetworkAddress::IPv4(value.into()) + NetworkAddress::IPv4(value) } } impl From for NetworkAddress { fn from(value: net::SocketAddrV6) -> Self { - NetworkAddress::IPv6(value.into()) + NetworkAddress::IPv6(value) } } @@ -158,47 +84,3 @@ impl From for NetworkAddress { } } } - -impl<'de> Deserialize<'de> for NetworkAddress { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let mut value = Value::deserialize(deserializer)?; - let addr_type = utils::get_internal_val_from_map(&mut value, "type", Value::get_u8, "u8")?; - - Ok(match addr_type { - 1 => NetworkAddress::IPv4(IPv4Address::from_value(utils::get_field_from_map( - &mut value, "addr", - )?)?), - 2 => NetworkAddress::IPv6(IPv6Address::from_value(utils::get_field_from_map( - &mut value, "addr", - )?)?), - _ => { - return Err(de::Error::custom( - "Network address type currently unsupported", - )) - } - }) - } -} - -impl Serialize for NetworkAddress { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut state = serializer.serialize_struct("", 2)?; - match self { - NetworkAddress::IPv4(v) => { - state.serialize_field("type", &1_u8)?; - state.serialize_field("addr", v)?; - } - NetworkAddress::IPv6(v) => { - state.serialize_field("type", &2_u8)?; - state.serialize_field("addr", v)?; - } - } - state.end() - } -} diff --git a/net/monero-wire/src/network_address/builder.rs b/net/monero-wire/src/network_address/builder.rs new file mode 100644 index 0000000..e67f10d --- /dev/null +++ b/net/monero-wire/src/network_address/builder.rs @@ -0,0 +1,167 @@ +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6}; + +use epee_encoding::{ + error::Error, + io::{Read, Write}, + read_epee_value, write_field, EpeeObject, EpeeObjectBuilder, EpeeValue, +}; + +use super::NetworkAddress; + +impl EpeeObject for NetworkAddress { + type Builder = NetworkAddressBuilder; + + fn number_of_fields(&self) -> u64 { + 2 + } + + fn write_fields(&self, w: &mut W) -> epee_encoding::error::Result<()> { + match self { + NetworkAddress::IPv4(ip) => { + write_field(&1_u8, "type", w)?; + let addr = NetworkAddressWriter { + host: ("m_ip", &u32::from_be_bytes(ip.ip().octets())), + port: ("m_port", ip.port()), + }; + write_field(&addr, "addr", w) + } + NetworkAddress::IPv6(ip) => { + write_field(&2_u8, "type", w)?; + let addr = NetworkAddressWriter { + host: ("addr", &ip.ip().octets()), + port: ("m_port", ip.port()), + }; + write_field(&addr, "addr", w) + } + } + } +} + +struct NetworkAddressWriter<'a, T> { + host: (&'static str, &'a T), + port: (&'static str, u16), +} + +#[derive(Default)] +struct NetworkAddressWBuilder; + +impl<'a, T> EpeeObjectBuilder> for NetworkAddressWBuilder { + fn add_field(&mut self, _name: &str, _r: &mut R) -> epee_encoding::Result { + panic!("Not used") + } + + fn finish(self) -> epee_encoding::Result> { + panic!("Not used") + } +} + +impl<'a, T: EpeeValue> EpeeObject for NetworkAddressWriter<'a, T> { + type Builder = NetworkAddressWBuilder; + + fn number_of_fields(&self) -> u64 { + 2 + } + + fn write_fields(&self, w: &mut W) -> epee_encoding::Result<()> { + write_field(self.host.1, self.host.0, w)?; + write_field(&self.port.1, self.port.0, w) + } +} +#[derive(Default)] +struct NetworkAddressBuilderIntermediate { + m_ip: Option, + addr: Option<[u8; 16]>, + m_port: Option, + port: Option, + host_tor: Option<[u8; 63]>, + host_i2p: Option<[u8; 61]>, +} + +impl EpeeObject for NetworkAddressBuilderIntermediate { + type Builder = Self; + + fn number_of_fields(&self) -> u64 { + panic!("This is only used on deserialization") + } + + fn write_fields(&self, _w: &mut W) -> epee_encoding::error::Result<()> { + panic!("This is only used on deserialization") + } +} + +impl EpeeObjectBuilder for NetworkAddressBuilderIntermediate { + fn add_field(&mut self, name: &str, r: &mut R) -> epee_encoding::error::Result { + match name { + "m_ip" => self.m_ip = Some(read_epee_value(r)?), + "addr" => self.addr = Some(read_epee_value(r)?), + "m_port" => self.m_port = Some(read_epee_value(r)?), + "port" => self.port = Some(read_epee_value(r)?), + "host" => { + let host: Vec = read_epee_value(r)?; + if host.len() == 63 { + self.host_tor = Some(host.try_into().unwrap()); + } else if host.len() == 61 { + self.host_i2p = Some(host.try_into().unwrap()); + } + } + _ => return Ok(false), + } + + Ok(true) + } + + fn finish(self) -> epee_encoding::error::Result { + Ok(self) + } +} + +#[derive(Default)] +pub struct NetworkAddressBuilder { + ty: Option, + addr: Option, +} + +impl EpeeObjectBuilder for NetworkAddressBuilder { + fn add_field(&mut self, name: &str, r: &mut R) -> epee_encoding::error::Result { + match name { + "type" => self.ty = Some(read_epee_value(r)?), + "addr" => self.addr = Some(read_epee_value(r)?), + _ => return Ok(false), + } + + Ok(true) + } + fn finish(self) -> epee_encoding::error::Result { + let addr = self + .addr + .ok_or(Error::Format("Required field was not in data"))?; + + Ok( + match self + .ty + .ok_or(Error::Format("Required field was not in data"))? + { + 1 => NetworkAddress::IPv4(SocketAddrV4::new( + Ipv4Addr::from( + addr.m_ip + .ok_or(Error::Format("Required field was not in data"))?, + ), + addr.m_port + .ok_or(Error::Format("Required field was not in data"))?, + )), + 2 => NetworkAddress::IPv6(SocketAddrV6::new( + Ipv6Addr::from( + addr.addr + .ok_or(Error::Format("Required field was not in data"))?, + ), + addr.m_port + .ok_or(Error::Format("Required field was not in data"))?, + 0, + 0, + )), + // TODO: tor/ i2p addresses + _ => return Err(Error::Value("Unsupported network address")), + }, + ) + } +} diff --git a/net/monero-wire/src/utils.rs b/net/monero-wire/src/utils.rs deleted file mode 100644 index 5c07af2..0000000 --- a/net/monero-wire/src/utils.rs +++ /dev/null @@ -1,45 +0,0 @@ -use epee_serde::Value; - -pub(crate) fn zero_val>() -> T { - T::from(0_u8) -} - -pub(crate) fn default_true() -> bool { - true -} - -pub(crate) fn default_false() -> bool { - false -} - -pub(crate) fn get_field_from_map( - value: &mut Value, - field_name: &'static str, -) -> Result { - value - .get_and_remove(field_name) - .ok_or(serde::de::Error::missing_field(field_name)) -} - -pub(crate) fn get_internal_val(value: Value, get_fn: F, expected_ty: &str) -> Result -where - E: serde::de::Error, - F: Fn(Value) -> Option, -{ - let err = serde::de::Error::invalid_type(value.get_value_type_as_unexpected(), &expected_ty); - get_fn(value).ok_or(err) -} - -pub(crate) fn get_internal_val_from_map( - value: &mut Value, - field_name: &'static str, - get_fn: F, - expected_ty: &str, -) -> Result -where - E: serde::de::Error, - F: Fn(Value) -> Option, -{ - let val = get_field_from_map(value, field_name)?; - get_internal_val(val, get_fn, expected_ty) -}