From bfbafa4ed5eeb2b462bac1ecd73212d2a0fbcf09 Mon Sep 17 00:00:00 2001 From: Boog900 <54e72d8a-345f-4599-bd90-c6b9bc7d0ec5@aleeas.com> Date: Mon, 9 Oct 2023 21:09:14 +0100 Subject: [PATCH] change epee-encoding to monero-epee-bin-serde --- consensus/src/bin/scan_chain.rs | 12 +- consensus/src/rpc.rs | 71 ++--- consensus/src/rpc/discover.rs | 80 ++++++ net/levin/src/lib.rs | 7 +- net/monero-wire/Cargo.toml | 4 +- net/monero-wire/src/lib.rs | 5 +- net/monero-wire/src/messages.rs | 249 ------------------ .../src/messages/common/builders.rs | 67 ----- .../src/messages/common/serde_impls.rs | 106 -------- net/monero-wire/src/network_address.rs | 9 +- .../src/network_address/builder.rs | 167 ------------ net/monero-wire/src/p2p.rs | 195 ++++++++++++++ .../src/{messages => p2p}/admin.rs | 34 +-- .../src/{messages => p2p}/common.rs | 68 ++--- .../src/{messages => p2p}/protocol.rs | 32 ++- 15 files changed, 403 insertions(+), 703 deletions(-) create mode 100644 consensus/src/rpc/discover.rs delete mode 100644 net/monero-wire/src/messages.rs delete mode 100644 net/monero-wire/src/messages/common/builders.rs delete mode 100644 net/monero-wire/src/messages/common/serde_impls.rs delete mode 100644 net/monero-wire/src/network_address/builder.rs create mode 100644 net/monero-wire/src/p2p.rs rename net/monero-wire/src/{messages => p2p}/admin.rs (98%) rename net/monero-wire/src/{messages => p2p}/common.rs (81%) rename net/monero-wire/src/{messages => p2p}/protocol.rs (98%) diff --git a/consensus/src/bin/scan_chain.rs b/consensus/src/bin/scan_chain.rs index 62525388..6d4f52b6 100644 --- a/consensus/src/bin/scan_chain.rs +++ b/consensus/src/bin/scan_chain.rs @@ -15,7 +15,7 @@ use monero_consensus::{ Database, DatabaseRequest, DatabaseResponse, }; -const BATCH_SIZE: u64 = MAX_BLOCKS_IN_RANGE * 4; +const BATCH_SIZE: u64 = MAX_BLOCKS_IN_RANGE * 3; /// A cache which can keep chain state while scanning. /// @@ -153,17 +153,7 @@ async fn main() { "http://xmr-node.cakewallet.com:18081".to_string(), "http://node.sethforprivacy.com".to_string(), "http://nodex.monerujo.io:18081".to_string(), - //"http://node.community.rino.io:18081".to_string(), "http://nodes.hashvault.pro:18081".to_string(), - // "http://node.moneroworld.com:18089".to_string(), - "http://node.c3pool.com:18081".to_string(), - // - "http://xmr-node.cakewallet.com:18081".to_string(), - "http://node.sethforprivacy.com".to_string(), - "http://nodex.monerujo.io:18081".to_string(), - //"http://node.community.rino.io:18081".to_string(), - "http://nodes.hashvault.pro:18081".to_string(), - // "http://node.moneroworld.com:18089".to_string(), "http://node.c3pool.com:18081".to_string(), ]; diff --git a/consensus/src/rpc.rs b/consensus/src/rpc.rs index 4317882a..381eee97 100644 --- a/consensus/src/rpc.rs +++ b/consensus/src/rpc.rs @@ -13,6 +13,7 @@ use serde_json::json; use tower::balance::p2c::Balance; use tower::util::BoxService; use tower::ServiceExt; +use tracing::Instrument; use cuprate_common::BlockID; use monero_wire::common::{BlockCompleteEntry, TransactionBlobs}; @@ -22,7 +23,9 @@ use crate::block::weight::BlockWeightInfo; use crate::hardforks::BlockHFInfo; use crate::{DatabaseRequest, DatabaseResponse}; -pub const MAX_BLOCKS_IN_RANGE: u64 = 75; +mod discover; + +pub const MAX_BLOCKS_IN_RANGE: u64 = 200; pub const MAX_BLOCKS_HEADERS_IN_RANGE: u64 = 200; #[derive(Clone)] @@ -213,15 +216,17 @@ enum RpcState { } pub struct Rpc { rpc: Arc>>, + addr: String, rpc_state: RpcState, error_slot: Arc>>, } impl Rpc { pub fn new_http(addr: String) -> Rpc { - let http_rpc = HttpRpc::new(addr).unwrap(); + let http_rpc = HttpRpc::new(addr.clone()).unwrap(); Rpc { rpc: Arc::new(futures::lock::Mutex::new(http_rpc)), + addr, rpc_state: RpcState::Locked, error_slot: Arc::new(Mutex::new(None)), } @@ -257,19 +262,22 @@ impl tower::Service f panic!("poll_ready was not called first!"); }; + let span = tracing::info_span!("rpc_request", addr = &self.addr); + let err_slot = self.error_slot.clone(); match req { DatabaseRequest::BlockHash(height) => async move { let res: Result<_, RpcError> = rpc .get_block_hash(height as usize) - .map_ok(|hash| DatabaseResponse::BlockHash(hash)) + .map_ok(DatabaseResponse::BlockHash) .await; if let Err(e) = &res { *err_slot.lock().unwrap() = Some(e.clone()); } res.map_err(Into::into) } + .instrument(span) .boxed(), DatabaseRequest::ChainHeight => async move { let res: Result<_, RpcError> = rpc @@ -281,21 +289,32 @@ impl tower::Service f } res.map_err(Into::into) } + .instrument(span) .boxed(), - DatabaseRequest::BlockPOWInfo(id) => get_blocks_pow_info(id, rpc).boxed(), - DatabaseRequest::BlockWeights(id) => get_blocks_weight_info(id, rpc).boxed(), - DatabaseRequest::BlockHFInfo(id) => get_blocks_hf_info(id, rpc).boxed(), - DatabaseRequest::BlockHfInfoInRange(range) => { - get_blocks_hf_info_in_range(range, rpc).boxed() + DatabaseRequest::BlockPOWInfo(id) => { + get_blocks_pow_info(id, rpc).instrument(span).boxed() } + DatabaseRequest::BlockWeights(id) => { + get_blocks_weight_info(id, rpc).instrument(span).boxed() + } + DatabaseRequest::BlockHFInfo(id) => { + get_blocks_hf_info(id, rpc).instrument(span).boxed() + } + DatabaseRequest::BlockHfInfoInRange(range) => get_blocks_hf_info_in_range(range, rpc) + .instrument(span) + .boxed(), DatabaseRequest::BlockWeightsInRange(range) => { - get_blocks_weight_info_in_range(range, rpc).boxed() + get_blocks_weight_info_in_range(range, rpc) + .instrument(span) + .boxed() } - DatabaseRequest::BlockPOWInfoInRange(range) => { - get_blocks_pow_info_in_range(range, rpc).boxed() + DatabaseRequest::BlockPOWInfoInRange(range) => get_blocks_pow_info_in_range(range, rpc) + .instrument(span) + .boxed(), + DatabaseRequest::BlockBatchInRange(range) => { + get_blocks_in_range(range, rpc).instrument(span).boxed() } - DatabaseRequest::BlockBatchInRange(range) => get_blocks_in_range(range, rpc).boxed(), } } } @@ -334,20 +353,15 @@ async fn get_blocks_in_range( .map(|b| { Ok(( monero_serai::block::Block::read(&mut b.block.as_slice())?, - if let Some(txs) = b.txs { - match txs { - TransactionBlobs::Pruned(_) => { - return Err("node sent pruned txs!".into()) - } - TransactionBlobs::Normal(txs) => txs - .into_iter() - .map(|tx| { - monero_serai::transaction::Transaction::read(&mut tx.as_slice()) - }) - .collect::>()?, - } - } else { - vec![] + match b.txs { + TransactionBlobs::Pruned(_) => return Err("node sent pruned txs!".into()), + TransactionBlobs::Normal(txs) => txs + .into_iter() + .map(|tx| { + monero_serai::transaction::Transaction::read(&mut tx.as_slice()) + }) + .collect::>()?, + TransactionBlobs::None => vec![], }, )) }) @@ -510,8 +524,3 @@ async fn get_blocks_hf_info_in_range( .collect(), )) } - -#[derive(Deserialize)] -pub struct BResponse { - pub blocks: Vec, -} diff --git a/consensus/src/rpc/discover.rs b/consensus/src/rpc/discover.rs new file mode 100644 index 00000000..da784261 --- /dev/null +++ b/consensus/src/rpc/discover.rs @@ -0,0 +1,80 @@ +use std::collections::HashSet; +use std::time::Duration; + +use futures::channel::mpsc::SendError; +use futures::stream::FuturesUnordered; +use futures::{channel::mpsc, SinkExt, Stream, StreamExt, TryFutureExt, TryStream}; +use monero_serai::rpc::HttpRpc; +use tokio::time::timeout; +use tower::discover::Change; +use tower::ServiceExt; +use tracing::instrument; + +use super::Rpc; +use crate::Database; + +#[instrument] +async fn check_rpc(addr: String) -> Option> { + tracing::debug!("Sending request to node."); + let rpc = HttpRpc::new(addr.clone()).ok()?; + // make sure the RPC is actually reachable + timeout(Duration::from_secs(2), rpc.get_height()) + .await + .ok()? + .ok()?; + + tracing::debug!("Node sent ok response."); + + Some(Rpc::new_http(addr)) +} + +struct RPCDiscover { + rpc: T, + initial_list: Vec, + ok_channel: mpsc::Sender>>, + already_connected: HashSet, +} + +impl RPCDiscover { + async fn found_rpc(&mut self, rpc: Rpc) -> Result<(), SendError> { + if self.already_connected.contains(&rpc.addr) { + return Ok(()); + } + + tracing::info!("Found node to connect to: {}", &rpc.addr); + + let addr = rpc.addr.clone(); + self.ok_channel + .send(Change::Insert(self.already_connected.len(), rpc)) + .await?; + self.already_connected.insert(addr); + + Ok(()) + } + + pub async fn run(mut self) { + loop { + if !self.initial_list.is_empty() { + let mut fut = + FuturesUnordered::from_iter(self.initial_list.drain(..).map(check_rpc)); + + while let Some(res) = fut.next().await { + if let Some(rpc) = res { + if self.found_rpc(rpc).await.is_err() { + tracing::info!("Stopping RPC discover channel closed!"); + return; + } + } + } + } + + if self.already_connected.len() > 100 { + tracing::info!("Stopping RPC discover, connected to 100 nodes!"); + } + + tokio::time::sleep(Duration::from_secs(2)).await + + // TODO: RPC request to get more peers + } + } +} diff --git a/net/levin/src/lib.rs b/net/levin/src/lib.rs index 6bb8cb68..7442f8bb 100644 --- a/net/levin/src/lib.rs +++ b/net/levin/src/lib.rs @@ -60,8 +60,11 @@ pub enum BucketError { #[error("Levin fragmented message was invalid: {0}")] InvalidFragmentedMessage(&'static str), /// Error decoding the body - #[error("Error decoding bucket body")] - BodyDecodingError(Box), + #[error("Error decoding bucket body: {0}")] + BodyDecodingError(Box), + /// The levin command is unknown + #[error("The levin command is unknown")] + UnknownCommand, /// I/O error #[error("I/O error: {0}")] IO(#[from] std::io::Error), diff --git a/net/monero-wire/Cargo.toml b/net/monero-wire/Cargo.toml index b6f787b3..7ad6e5bc 100644 --- a/net/monero-wire/Cargo.toml +++ b/net/monero-wire/Cargo.toml @@ -9,10 +9,10 @@ repository = "https://github.com/SyntheticBird45/cuprate/tree/main/net/monero-wi [dependencies] levin-cuprate = {path="../levin"} -epee-encoding = { git = "https://github.com/boog900/epee-encoding"} monero-epee-bin-serde = {git = "https://github.com/monero-rs/monero-epee-bin-serde.git", rev="e4a585a"} serde = {version = "1", features = ["derive"]} -serde_with = "3" +serde_bytes = "0.11" +thiserror = "1" [dev-dependencies] hex = "0.4.3" diff --git a/net/monero-wire/src/lib.rs b/net/monero-wire/src/lib.rs index 36336e90..232b86cc 100644 --- a/net/monero-wire/src/lib.rs +++ b/net/monero-wire/src/lib.rs @@ -29,12 +29,11 @@ #![deny(unused_mut)] //#![deny(missing_docs)] -pub mod messages; pub mod network_address; +pub mod p2p; mod serde_helpers; pub use network_address::NetworkAddress; - -pub use messages::*; +pub use p2p::*; pub type MoneroWireCodec = levin_cuprate::codec::LevinMessageCodec; diff --git a/net/monero-wire/src/messages.rs b/net/monero-wire/src/messages.rs deleted file mode 100644 index 14e8028c..00000000 --- a/net/monero-wire/src/messages.rs +++ /dev/null @@ -1,249 +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. -// - -//! 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::{ - HandshakeRequest, HandshakeResponse, PingResponse, SupportFlagsResponse, TimedSyncRequest, - TimedSyncResponse, -}; -pub use common::{BasicNodeData, CoreSyncData, PeerListEntryBase}; -pub use protocol::{ - ChainRequest, ChainResponse, FluffyMissingTransactionsRequest, GetObjectsRequest, - GetObjectsResponse, GetTxPoolCompliment, NewBlock, NewFluffyBlock, NewTransactions, -}; - -pub enum ProtocolMessage { - NewBlock(NewBlock), - NewFluffyBlock(NewFluffyBlock), - GetObjectsRequest(GetObjectsRequest), - GetObjectsResponse(GetObjectsResponse), - ChainRequest(ChainRequest), - ChainEntryResponse(ChainResponse), - NewTransactions(NewTransactions), - FluffyMissingTransactionsRequest(FluffyMissingTransactionsRequest), - GetTxPoolCompliment(GetTxPoolCompliment), -} - -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(()) - } -} - -pub enum RequestMessage { - Handshake(HandshakeRequest), - Ping, - SupportFlags, - TimedSync(TimedSyncRequest), -} - -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/common/builders.rs b/net/monero-wire/src/messages/common/builders.rs deleted file mode 100644 index d94e4bd2..00000000 --- a/net/monero-wire/src/messages/common/builders.rs +++ /dev/null @@ -1,67 +0,0 @@ -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/common/serde_impls.rs b/net/monero-wire/src/messages/common/serde_impls.rs deleted file mode 100644 index 88a9ffad..00000000 --- a/net/monero-wire/src/messages/common/serde_impls.rs +++ /dev/null @@ -1,106 +0,0 @@ -use serde::de::{Error, SeqAccess}; -use serde::ser::SerializeSeq; -use serde::{ - de::{Deserialize, Visitor}, - Deserializer, Serialize, Serializer, -}; -use std::fmt::Formatter; - -use super::TransactionBlobs; - -impl<'de> Deserialize<'de> for TransactionBlobs { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct TBVisitor; - - impl<'de> Visitor<'de> for TBVisitor { - type Value = TransactionBlobs; - - fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { - write!(formatter, "A sequence of transactions blob") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let mut normal = Vec::new(); - //let pruned = Vec::new(); - - while let Some(val) = seq.next_element::()? { - match val { - SingleBlob::Pruned(tx) => normal.push(tx), - } - } - - Ok(TransactionBlobs::Normal(normal)) - } - } - - deserializer.deserialize_any(TBVisitor) - } -} - -impl Serialize for TransactionBlobs { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - TransactionBlobs::Pruned(_) => todo!(), - TransactionBlobs::Normal(txs) => { - let mut seq_ser = serializer.serialize_seq(Some(txs.len()))?; - for tx in txs { - seq_ser.serialize_element(tx)?; - } - seq_ser.end() - } - } - } -} - -enum SingleBlob { - Pruned(Vec), -} - -impl<'de> Deserialize<'de> for SingleBlob { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct TBDSVisitor; - - impl<'de> Visitor<'de> for TBDSVisitor { - type Value = SingleBlob; - - fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { - write!(formatter, "A single transaction blob") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: Error, - { - Ok(SingleBlob::Pruned(v.into())) - } - - fn visit_byte_buf(self, v: Vec) -> Result - where - E: Error, - { - Ok(SingleBlob::Pruned(v)) - } - - fn visit_newtype_struct(self, deserializer: D) -> Result - where - D: Deserializer<'de>, - { - todo!("Pruned blobs") - } - } - - deserializer.deserialize_any(TBDSVisitor) - } -} diff --git a/net/monero-wire/src/network_address.rs b/net/monero-wire/src/network_address.rs index 69365b9f..d1aab440 100644 --- a/net/monero-wire/src/network_address.rs +++ b/net/monero-wire/src/network_address.rs @@ -20,7 +20,10 @@ use std::net::{SocketAddrV4, SocketAddrV6}; use std::{hash::Hash, net}; -mod builder; +use serde::{Deserialize, Serialize}; + +mod serde_helper; +use serde_helper::*; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum NetZone { @@ -31,7 +34,9 @@ pub enum NetZone { /// A network address which can be encoded into the format required /// to send to other Monero peers. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(try_from = "TaggedNetworkAddress")] +#[serde(into = "TaggedNetworkAddress")] pub enum NetworkAddress { /// IPv4 IPv4(SocketAddrV4), diff --git a/net/monero-wire/src/network_address/builder.rs b/net/monero-wire/src/network_address/builder.rs deleted file mode 100644 index e67f10db..00000000 --- a/net/monero-wire/src/network_address/builder.rs +++ /dev/null @@ -1,167 +0,0 @@ -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/p2p.rs b/net/monero-wire/src/p2p.rs new file mode 100644 index 00000000..48b316a8 --- /dev/null +++ b/net/monero-wire/src/p2p.rs @@ -0,0 +1,195 @@ +// 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. +// + +//! 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; + +use admin::*; +pub use common::{BasicNodeData, CoreSyncData, PeerListEntryBase}; +use protocol::*; + +fn decode_message( + ret: impl FnOnce(T) -> Ret, + buf: &[u8], +) -> Result { + let t = monero_epee_bin_serde::from_bytes(buf) + .map_err(|e| BucketError::BodyDecodingError(e.into()))?; + Ok(ret(t)) +} + +fn build_message( + id: u32, + val: &T, + builder: &mut BucketBuilder, +) -> Result<(), BucketError> { + builder.set_command(id); + builder.set_body( + monero_epee_bin_serde::to_bytes(val) + .map_err(|e| BucketError::BodyDecodingError(e.into()))?, + ); + Ok(()) +} + +pub enum ProtocolMessage { + NewBlock(NewBlock), + NewFluffyBlock(NewFluffyBlock), + GetObjectsRequest(GetObjectsRequest), + GetObjectsResponse(GetObjectsResponse), + ChainRequest(ChainRequest), + ChainEntryResponse(ChainResponse), + NewTransactions(NewTransactions), + FluffyMissingTransactionsRequest(FluffyMissingTransactionsRequest), + GetTxPoolCompliment(GetTxPoolCompliment), +} + +impl ProtocolMessage { + fn decode(buf: &[u8], command: u32) -> Result { + Ok(match command { + 2001 => decode_message(ProtocolMessage::NewBlock, buf)?, + 2002 => decode_message(ProtocolMessage::NewTransactions, buf)?, + 2003 => decode_message(ProtocolMessage::GetObjectsRequest, buf)?, + 2004 => decode_message(ProtocolMessage::GetObjectsResponse, buf)?, + 2006 => decode_message(ProtocolMessage::ChainRequest, buf)?, + 2007 => decode_message(ProtocolMessage::ChainEntryResponse, buf)?, + 2008 => decode_message(ProtocolMessage::NewFluffyBlock, buf)?, + 2009 => decode_message(ProtocolMessage::FluffyMissingTransactionsRequest, buf)?, + 2010 => decode_message(ProtocolMessage::GetTxPoolCompliment, buf)?, + _ => return Err(BucketError::UnknownCommand), + }) + } + + fn build(&self, builder: &mut BucketBuilder) -> Result<(), BucketError> { + match self { + ProtocolMessage::NewBlock(val) => build_message(2001, val, builder)?, + ProtocolMessage::NewTransactions(val) => build_message(2002, val, builder)?, + ProtocolMessage::GetObjectsRequest(val) => build_message(2003, val, builder)?, + ProtocolMessage::GetObjectsResponse(val) => build_message(2004, val, builder)?, + ProtocolMessage::ChainRequest(val) => build_message(2006, val, builder)?, + ProtocolMessage::ChainEntryResponse(val) => build_message(2007, &val, builder)?, + ProtocolMessage::NewFluffyBlock(val) => build_message(2008, val, builder)?, + ProtocolMessage::FluffyMissingTransactionsRequest(val) => { + build_message(2009, val, builder)? + } + ProtocolMessage::GetTxPoolCompliment(val) => build_message(2010, val, builder)?, + } + Ok(()) + } +} + +pub enum RequestMessage { + Handshake(HandshakeRequest), + Ping, + SupportFlags, + TimedSync(TimedSyncRequest), +} + +impl RequestMessage { + fn decode(buf: &[u8], command: u32) -> Result { + Ok(match command { + 1001 => decode_message(RequestMessage::Handshake, buf)?, + 1002 => decode_message(RequestMessage::TimedSync, buf)?, + 1003 => RequestMessage::Ping, + 1007 => RequestMessage::SupportFlags, + _ => return Err(BucketError::UnknownCommand), + }) + } + + fn build(&self, builder: &mut BucketBuilder) -> Result<(), BucketError> { + match self { + RequestMessage::Handshake(val) => build_message(1001, val, builder)?, + RequestMessage::TimedSync(val) => build_message(1002, val, builder)?, + 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 => decode_message(ResponseMessage::Handshake, buf)?, + 1002 => decode_message(ResponseMessage::TimedSync, buf)?, + 1003 => decode_message(ResponseMessage::Ping, buf)?, + 1007 => decode_message(ResponseMessage::SupportFlags, buf)?, + _ => return Err(BucketError::UnknownCommand), + }) + } + + fn build(&self, builder: &mut BucketBuilder) -> Result<(), BucketError> { + match self { + ResponseMessage::Handshake(val) => build_message(1001, val, builder)?, + ResponseMessage::TimedSync(val) => build_message(1002, val, builder)?, + ResponseMessage::Ping(val) => build_message(1003, val, builder)?, + ResponseMessage::SupportFlags(val) => build_message(1007, val, builder)?, + } + 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)?), + MessageType::Response => Message::Response(ResponseMessage::decode(body, command)?), + MessageType::Notification => Message::Protocol(ProtocolMessage::decode(body, command)?), + }) + } + + 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) + } + Message::Request(req) => { + builder.set_message_type(MessageType::Request); + builder.set_return_code(0); + req.build(builder) + } + Message::Response(res) => { + builder.set_message_type(MessageType::Response); + builder.set_return_code(1); + res.build(builder) + } + } + } +} diff --git a/net/monero-wire/src/messages/admin.rs b/net/monero-wire/src/p2p/admin.rs similarity index 98% rename from net/monero-wire/src/messages/admin.rs rename to net/monero-wire/src/p2p/admin.rs index d60040a9..9a8e842a 100644 --- a/net/monero-wire/src/messages/admin.rs +++ b/net/monero-wire/src/p2p/admin.rs @@ -18,12 +18,12 @@ //! Admin message requests must be responded to in order unlike //! protocol messages. -use epee_encoding::EpeeObject; +use serde::{Deserialize, Serialize}; use super::common::{BasicNodeData, CoreSyncData, PeerListEntryBase, PeerSupportFlags}; /// A Handshake Request -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct HandshakeRequest { /// Basic Node Data pub node_data: BasicNodeData, @@ -32,30 +32,31 @@ pub struct HandshakeRequest { } /// A Handshake Response -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct HandshakeResponse { /// Basic Node Data pub node_data: BasicNodeData, /// Core Sync Data pub payload_data: CoreSyncData, /// PeerList - #[epee_default(Vec::new())] + #[serde(default = "Vec::new")] pub local_peerlist_new: Vec, } /// A TimedSync Request -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct TimedSyncRequest { /// Core Sync Data pub payload_data: CoreSyncData, } /// A TimedSync Response -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct TimedSyncResponse { /// Core Sync Data pub payload_data: CoreSyncData, /// PeerList + #[serde(default = "Vec::new")] pub local_peerlist_new: Vec, } @@ -63,7 +64,7 @@ pub struct TimedSyncResponse { pub const PING_OK_RESPONSE_STATUS_TEXT: &str = "OK"; /// A Ping Response -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct PingResponse { /// Status: should be `PING_OK_RESPONSE_STATUS_TEXT` pub status: String, @@ -72,10 +73,9 @@ pub struct PingResponse { } /// A Support Flags Response -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SupportFlagsResponse { /// Support Flags - #[epee_try_from_into(u32)] pub support_flags: PeerSupportFlags, } @@ -83,7 +83,7 @@ pub struct SupportFlagsResponse { mod tests { use super::{BasicNodeData, CoreSyncData, HandshakeRequest, HandshakeResponse}; - use crate::messages::common::PeerSupportFlags; + use crate::p2p::common::PeerSupportFlags; #[test] fn serde_handshake_req() { @@ -101,7 +101,7 @@ 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_encoding::from_bytes(&bytes).unwrap(); + let handshake: HandshakeRequest = monero_epee_bin_serde::from_bytes(&bytes).unwrap(); let basic_node_data = BasicNodeData { my_port: 0, network_id: [ @@ -128,8 +128,9 @@ mod tests { assert_eq!(basic_node_data, handshake.node_data); assert_eq!(core_sync_data, handshake.payload_data); - let encoded_bytes = epee_encoding::to_bytes(&handshake).unwrap(); - let handshake_2: HandshakeRequest = epee_encoding::from_bytes(&encoded_bytes).unwrap(); + let encoded_bytes = monero_epee_bin_serde::to_bytes(&handshake).unwrap(); + let handshake_2: HandshakeRequest = + monero_epee_bin_serde::from_bytes(&encoded_bytes).unwrap(); assert_eq!(handshake, handshake_2); } @@ -905,7 +906,7 @@ 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_encoding::from_bytes(&bytes).unwrap(); + let handshake: HandshakeResponse = monero_epee_bin_serde::from_bytes(&bytes).unwrap(); let basic_node_data = BasicNodeData { my_port: 18080, @@ -934,8 +935,9 @@ mod tests { assert_eq!(core_sync_data, handshake.payload_data); assert_eq!(250, handshake.local_peerlist_new.len()); - let encoded_bytes = epee_encoding::to_bytes(&handshake).unwrap(); - let handshake_2: HandshakeResponse = epee_encoding::from_bytes(&encoded_bytes).unwrap(); + let encoded_bytes = monero_epee_bin_serde::to_bytes(&handshake).unwrap(); + let handshake_2: HandshakeResponse = + monero_epee_bin_serde::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/p2p/common.rs similarity index 81% rename from net/monero-wire/src/messages/common.rs rename to net/monero-wire/src/p2p/common.rs index 5c69882a..80376948 100644 --- a/net/monero-wire/src/messages/common.rs +++ b/net/monero-wire/src/p2p/common.rs @@ -14,20 +14,17 @@ // //! Common types that are used across multiple messages. -// -use epee_encoding::EpeeObject; + use serde::{Deserialize, Serialize}; -use serde_with::{serde_as, Bytes}; +use serde_bytes::ByteBuf; use crate::{ serde_helpers::{default_false, default_zero}, NetworkAddress, }; -mod builders; -mod serde_impls; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(transparent)] pub struct PeerSupportFlags(u32); impl From for PeerSupportFlags { @@ -70,7 +67,7 @@ impl From for PeerSupportFlags { } /// Basic Node Data, information on the connected peer -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct BasicNodeData { /// Port pub my_port: u32, @@ -80,39 +77,38 @@ pub struct BasicNodeData { pub peer_id: u64, /// The Peers Support Flags /// (If this is not in the message the default is 0) - #[epee_try_from_into(u32)] - #[epee_default(0_u32)] + #[serde(default = "default_zero")] pub support_flags: PeerSupportFlags, /// RPC Port /// (If this is not in the message the default is 0) - #[epee_default(0)] + #[serde(default = "default_zero")] pub rpc_port: u16, /// RPC Credits Per Hash /// (If this is not in the message the default is 0) - #[epee_default(0)] + #[serde(default = "default_zero")] pub rpc_credits_per_hash: u32, } /// Core Sync Data, information on the sync state of a peer -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] 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 - #[epee_default(0)] + #[serde(default = "default_zero")] 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) - #[epee_default(0)] + #[serde(default = "default_zero")] pub pruning_seed: u32, /// Hash of the top block pub top_id: [u8; 32], /// Version of the top block - #[epee_default(0)] + #[serde(default = "default_zero")] pub top_version: u8, } @@ -145,23 +141,23 @@ impl CoreSyncData { /// PeerListEntryBase, information kept on a peer which will be entered /// in a peer list/store. -#[derive(Clone, Copy, EpeeObject, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct PeerListEntryBase { /// The Peer Address pub adr: NetworkAddress, /// The Peer ID pub id: u64, /// The last Time The Peer Was Seen - #[epee_default(0)] + #[serde(default = "default_zero")] pub last_seen: i64, /// The Pruning Seed - #[epee_default(0)] + #[serde(default = "default_zero")] pub pruning_seed: u32, /// The RPC port - #[epee_default(0)] + #[serde(default = "default_zero")] pub rpc_port: u16, /// The RPC credits per hash - #[epee_default(0)] + #[serde(default = "default_zero")] pub rpc_credits_per_hash: u32, } @@ -173,18 +169,21 @@ impl std::hash::Hash for PeerListEntryBase { } /// A pruned tx with the hash of the missing prunable data -#[derive(Clone, Debug, EpeeObject, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct PrunedTxBlobEntry { /// The Tx - pub tx: Vec, + pub tx: ByteBuf, /// The Prunable Tx Hash pub prunable_hash: [u8; 32], } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(untagged)] pub enum TransactionBlobs { Pruned(Vec), - Normal(Vec>), + Normal(Vec), + #[serde(skip_serializing)] + None, } impl TransactionBlobs { @@ -192,33 +191,36 @@ impl TransactionBlobs { match self { TransactionBlobs::Normal(txs) => txs.len(), TransactionBlobs::Pruned(txs) => txs.len(), + TransactionBlobs::None => 0, } } pub fn is_empty(&self) -> bool { self.len() == 0 } + + fn none() -> TransactionBlobs { + TransactionBlobs::None + } } /// A Block that can contain transactions -#[serde_as] -#[derive(Clone, Debug, EpeeObject, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct BlockCompleteEntry { /// True if tx data is pruned - #[epee_default(false)] #[serde(default = "default_false")] pub pruned: bool, /// The Block - #[serde_as(as = "Bytes")] + //#[serde_as(as = "Bytes")] + #[serde(with = "serde_bytes")] pub block: Vec, /// The Block Weight/Size - #[epee_default(0)] #[serde(default = "default_zero")] pub block_weight: u64, /// The blocks txs - #[epee_default(None)] - #[serde(skip_serializing_if = "Option::is_none")] - pub txs: Option, + #[serde(skip_serializing_if = "TransactionBlobs::is_empty")] + #[serde(default = "TransactionBlobs::none")] + pub txs: TransactionBlobs, } #[cfg(test)] diff --git a/net/monero-wire/src/messages/protocol.rs b/net/monero-wire/src/p2p/protocol.rs similarity index 98% rename from net/monero-wire/src/messages/protocol.rs rename to net/monero-wire/src/p2p/protocol.rs index 14c59613..8eaaa014 100644 --- a/net/monero-wire/src/messages/protocol.rs +++ b/net/monero-wire/src/p2p/protocol.rs @@ -18,12 +18,13 @@ //! Protocol message requests don't have to be responded to in order unlike //! admin messages. -use epee_encoding::EpeeObject; +use serde::{Deserialize, Serialize}; use super::common::BlockCompleteEntry; +use crate::serde_helpers::*; /// A block that SHOULD have transactions -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct NewBlock { /// Block with txs pub b: BlockCompleteEntry, @@ -32,51 +33,53 @@ pub struct NewBlock { } /// New Tx Pool Transactions -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct NewTransactions { /// Tx Blobs pub txs: Vec>, /// Dandelionpp true if fluff - backwards compatible mode is fluff - #[epee_default(true)] + #[serde(default = "default_true")] pub dandelionpp_fluff: bool, /// Padding - #[epee_alt_name("_")] + #[serde(rename = "_")] pub padding: Vec, } /// A Request For Blocks -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct GetObjectsRequest { /// Block hashes we want pub blocks: Vec<[u8; 32]>, /// Pruned - #[epee_default(false)] + #[serde(default = "default_false")] pub pruned: bool, } /// A Blocks Response -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct GetObjectsResponse { /// Blocks + // We dont need to give this a default value as there always is at least 1 block pub blocks: Vec, /// Missed IDs + #[serde(default = "Vec::new")] pub missed_ids: Vec<[u8; 32]>, /// The height of the peers blockchain pub current_blockchain_height: u64, } /// A Chain Request -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ChainRequest { /// Block IDs pub block_ids: Vec<[u8; 32]>, /// Prune - #[epee_default(false)] + #[serde(default = "default_false")] pub prune: bool, } /// A Chain Response -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ChainResponse { /// Start Height pub start_height: u64, @@ -123,7 +126,7 @@ impl ChainResponse { } /// A Block that doesn't have transactions unless requested -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct NewFluffyBlock { /// Block which might have transactions pub b: BlockCompleteEntry, @@ -132,7 +135,7 @@ pub struct NewFluffyBlock { } /// A request for Txs we are missing from our TxPool -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FluffyMissingTransactionsRequest { /// The Block we are missing the Txs in pub block_hash: [u8; 32], @@ -143,9 +146,10 @@ pub struct FluffyMissingTransactionsRequest { } /// TxPoolCompliment -#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct GetTxPoolCompliment { /// Tx Hashes + #[serde(default = "Vec::new")] pub hashes: Vec<[u8; 32]>, }