change epee-encoding to monero-epee-bin-serde

This commit is contained in:
Boog900 2023-10-09 21:09:14 +01:00
parent 20f6af7951
commit bfbafa4ed5
No known key found for this signature in database
GPG key ID: 5401367FB7302004
15 changed files with 403 additions and 703 deletions

View file

@ -15,7 +15,7 @@ use monero_consensus::{
Database, DatabaseRequest, DatabaseResponse, 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. /// 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://xmr-node.cakewallet.com:18081".to_string(),
"http://node.sethforprivacy.com".to_string(), "http://node.sethforprivacy.com".to_string(),
"http://nodex.monerujo.io:18081".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://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(), "http://node.c3pool.com:18081".to_string(),
]; ];

View file

@ -13,6 +13,7 @@ use serde_json::json;
use tower::balance::p2c::Balance; use tower::balance::p2c::Balance;
use tower::util::BoxService; use tower::util::BoxService;
use tower::ServiceExt; use tower::ServiceExt;
use tracing::Instrument;
use cuprate_common::BlockID; use cuprate_common::BlockID;
use monero_wire::common::{BlockCompleteEntry, TransactionBlobs}; use monero_wire::common::{BlockCompleteEntry, TransactionBlobs};
@ -22,7 +23,9 @@ use crate::block::weight::BlockWeightInfo;
use crate::hardforks::BlockHFInfo; use crate::hardforks::BlockHFInfo;
use crate::{DatabaseRequest, DatabaseResponse}; 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; pub const MAX_BLOCKS_HEADERS_IN_RANGE: u64 = 200;
#[derive(Clone)] #[derive(Clone)]
@ -213,15 +216,17 @@ enum RpcState<R: RpcConnection> {
} }
pub struct Rpc<R: RpcConnection> { pub struct Rpc<R: RpcConnection> {
rpc: Arc<futures::lock::Mutex<monero_serai::rpc::Rpc<R>>>, rpc: Arc<futures::lock::Mutex<monero_serai::rpc::Rpc<R>>>,
addr: String,
rpc_state: RpcState<R>, rpc_state: RpcState<R>,
error_slot: Arc<Mutex<Option<RpcError>>>, error_slot: Arc<Mutex<Option<RpcError>>>,
} }
impl Rpc<HttpRpc> { impl Rpc<HttpRpc> {
pub fn new_http(addr: String) -> Rpc<HttpRpc> { pub fn new_http(addr: String) -> Rpc<HttpRpc> {
let http_rpc = HttpRpc::new(addr).unwrap(); let http_rpc = HttpRpc::new(addr.clone()).unwrap();
Rpc { Rpc {
rpc: Arc::new(futures::lock::Mutex::new(http_rpc)), rpc: Arc::new(futures::lock::Mutex::new(http_rpc)),
addr,
rpc_state: RpcState::Locked, rpc_state: RpcState::Locked,
error_slot: Arc::new(Mutex::new(None)), error_slot: Arc::new(Mutex::new(None)),
} }
@ -257,19 +262,22 @@ impl<R: RpcConnection + Send + Sync + 'static> tower::Service<DatabaseRequest> f
panic!("poll_ready was not called first!"); panic!("poll_ready was not called first!");
}; };
let span = tracing::info_span!("rpc_request", addr = &self.addr);
let err_slot = self.error_slot.clone(); let err_slot = self.error_slot.clone();
match req { match req {
DatabaseRequest::BlockHash(height) => async move { DatabaseRequest::BlockHash(height) => async move {
let res: Result<_, RpcError> = rpc let res: Result<_, RpcError> = rpc
.get_block_hash(height as usize) .get_block_hash(height as usize)
.map_ok(|hash| DatabaseResponse::BlockHash(hash)) .map_ok(DatabaseResponse::BlockHash)
.await; .await;
if let Err(e) = &res { if let Err(e) = &res {
*err_slot.lock().unwrap() = Some(e.clone()); *err_slot.lock().unwrap() = Some(e.clone());
} }
res.map_err(Into::into) res.map_err(Into::into)
} }
.instrument(span)
.boxed(), .boxed(),
DatabaseRequest::ChainHeight => async move { DatabaseRequest::ChainHeight => async move {
let res: Result<_, RpcError> = rpc let res: Result<_, RpcError> = rpc
@ -281,21 +289,32 @@ impl<R: RpcConnection + Send + Sync + 'static> tower::Service<DatabaseRequest> f
} }
res.map_err(Into::into) res.map_err(Into::into)
} }
.instrument(span)
.boxed(), .boxed(),
DatabaseRequest::BlockPOWInfo(id) => get_blocks_pow_info(id, rpc).boxed(), DatabaseRequest::BlockPOWInfo(id) => {
DatabaseRequest::BlockWeights(id) => get_blocks_weight_info(id, rpc).boxed(), get_blocks_pow_info(id, rpc).instrument(span).boxed()
DatabaseRequest::BlockHFInfo(id) => get_blocks_hf_info(id, rpc).boxed(),
DatabaseRequest::BlockHfInfoInRange(range) => {
get_blocks_hf_info_in_range(range, rpc).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) => { 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) => { DatabaseRequest::BlockPOWInfoInRange(range) => get_blocks_pow_info_in_range(range, rpc)
get_blocks_pow_info_in_range(range, rpc).boxed() .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<R: RpcConnection>(
.map(|b| { .map(|b| {
Ok(( Ok((
monero_serai::block::Block::read(&mut b.block.as_slice())?, monero_serai::block::Block::read(&mut b.block.as_slice())?,
if let Some(txs) = b.txs { match b.txs {
match txs { TransactionBlobs::Pruned(_) => return Err("node sent pruned txs!".into()),
TransactionBlobs::Pruned(_) => {
return Err("node sent pruned txs!".into())
}
TransactionBlobs::Normal(txs) => txs TransactionBlobs::Normal(txs) => txs
.into_iter() .into_iter()
.map(|tx| { .map(|tx| {
monero_serai::transaction::Transaction::read(&mut tx.as_slice()) monero_serai::transaction::Transaction::read(&mut tx.as_slice())
}) })
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
} TransactionBlobs::None => vec![],
} else {
vec![]
}, },
)) ))
}) })
@ -510,8 +524,3 @@ async fn get_blocks_hf_info_in_range<R: RpcConnection>(
.collect(), .collect(),
)) ))
} }
#[derive(Deserialize)]
pub struct BResponse {
pub blocks: Vec<BlockCompleteEntry>,
}

View file

@ -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<Rpc<HttpRpc>> {
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<T> {
rpc: T,
initial_list: Vec<String>,
ok_channel: mpsc::Sender<Change<usize, Rpc<HttpRpc>>>,
already_connected: HashSet<String>,
}
impl<T: Database> RPCDiscover<T> {
async fn found_rpc(&mut self, rpc: Rpc<HttpRpc>) -> 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
}
}
}

View file

@ -60,8 +60,11 @@ pub enum BucketError {
#[error("Levin fragmented message was invalid: {0}")] #[error("Levin fragmented message was invalid: {0}")]
InvalidFragmentedMessage(&'static str), InvalidFragmentedMessage(&'static str),
/// Error decoding the body /// Error decoding the body
#[error("Error decoding bucket body")] #[error("Error decoding bucket body: {0}")]
BodyDecodingError(Box<dyn Debug>), BodyDecodingError(Box<dyn std::error::Error>),
/// The levin command is unknown
#[error("The levin command is unknown")]
UnknownCommand,
/// I/O error /// I/O error
#[error("I/O error: {0}")] #[error("I/O error: {0}")]
IO(#[from] std::io::Error), IO(#[from] std::io::Error),

View file

@ -9,10 +9,10 @@ repository = "https://github.com/SyntheticBird45/cuprate/tree/main/net/monero-wi
[dependencies] [dependencies]
levin-cuprate = {path="../levin"} 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"} monero-epee-bin-serde = {git = "https://github.com/monero-rs/monero-epee-bin-serde.git", rev="e4a585a"}
serde = {version = "1", features = ["derive"]} serde = {version = "1", features = ["derive"]}
serde_with = "3" serde_bytes = "0.11"
thiserror = "1"
[dev-dependencies] [dev-dependencies]
hex = "0.4.3" hex = "0.4.3"

View file

@ -29,12 +29,11 @@
#![deny(unused_mut)] #![deny(unused_mut)]
//#![deny(missing_docs)] //#![deny(missing_docs)]
pub mod messages;
pub mod network_address; pub mod network_address;
pub mod p2p;
mod serde_helpers; mod serde_helpers;
pub use network_address::NetworkAddress; pub use network_address::NetworkAddress;
pub use p2p::*;
pub use messages::*;
pub type MoneroWireCodec = levin_cuprate::codec::LevinMessageCodec<Message>; pub type MoneroWireCodec = levin_cuprate::codec::LevinMessageCodec<Message>;

View file

@ -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<Self, epee_encoding::Error> {
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<Self, epee_encoding::Error> {
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<Self, epee_encoding::Error> {
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<Self, BucketError> {
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(())
}
}

View file

@ -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<W: Write>(&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<PrunedTxBlobEntry>),
Normal(Vec<Vec<u8>>),
}
impl EpeeObjectBuilder<TransactionBlobs> for TransactionBlobsBuilder {
fn add_field<R: Read>(&mut self, name: &str, r: &mut R) -> epee_encoding::error::Result<bool> {
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::<Vec<u8>>::read(r, &marker)?);
let _ = std::mem::replace(self, state);
}
InnerMarker::Object => {
let state =
TransactionBlobsBuilder::Pruned(Vec::<PrunedTxBlobEntry>::read(r, &marker)?);
let _ = std::mem::replace(self, state);
}
_ => return Err(Error::Format("Unexpected marker")),
}
Ok(true)
}
fn finish(self) -> epee_encoding::error::Result<TransactionBlobs> {
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)),
}
}
}

View file

@ -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<D>(deserializer: D) -> Result<Self, D::Error>
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut normal = Vec::new();
//let pruned = Vec::new();
while let Some(val) = seq.next_element::<SingleBlob>()? {
match val {
SingleBlob::Pruned(tx) => normal.push(tx),
}
}
Ok(TransactionBlobs::Normal(normal))
}
}
deserializer.deserialize_any(TBVisitor)
}
}
impl Serialize for TransactionBlobs {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<u8>),
}
impl<'de> Deserialize<'de> for SingleBlob {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
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<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: Error,
{
Ok(SingleBlob::Pruned(v.into()))
}
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
where
E: Error,
{
Ok(SingleBlob::Pruned(v))
}
fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
todo!("Pruned blobs")
}
}
deserializer.deserialize_any(TBDSVisitor)
}
}

View file

@ -20,7 +20,10 @@
use std::net::{SocketAddrV4, SocketAddrV6}; use std::net::{SocketAddrV4, SocketAddrV6};
use std::{hash::Hash, net}; use std::{hash::Hash, net};
mod builder; use serde::{Deserialize, Serialize};
mod serde_helper;
use serde_helper::*;
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum NetZone { pub enum NetZone {
@ -31,7 +34,9 @@ pub enum NetZone {
/// A network address which can be encoded into the format required /// A network address which can be encoded into the format required
/// to send to other Monero peers. /// 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 { pub enum NetworkAddress {
/// IPv4 /// IPv4
IPv4(SocketAddrV4), IPv4(SocketAddrV4),

View file

@ -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<W: Write>(&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<NetworkAddressWriter<'a, T>> for NetworkAddressWBuilder {
fn add_field<R: Read>(&mut self, _name: &str, _r: &mut R) -> epee_encoding::Result<bool> {
panic!("Not used")
}
fn finish(self) -> epee_encoding::Result<NetworkAddressWriter<'a, T>> {
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<W: Write>(&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<u32>,
addr: Option<[u8; 16]>,
m_port: Option<u16>,
port: Option<u16>,
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<W: Write>(&self, _w: &mut W) -> epee_encoding::error::Result<()> {
panic!("This is only used on deserialization")
}
}
impl EpeeObjectBuilder<NetworkAddressBuilderIntermediate> for NetworkAddressBuilderIntermediate {
fn add_field<R: Read>(&mut self, name: &str, r: &mut R) -> epee_encoding::error::Result<bool> {
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<u8> = 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<NetworkAddressBuilderIntermediate> {
Ok(self)
}
}
#[derive(Default)]
pub struct NetworkAddressBuilder {
ty: Option<u8>,
addr: Option<NetworkAddressBuilderIntermediate>,
}
impl EpeeObjectBuilder<NetworkAddress> for NetworkAddressBuilder {
fn add_field<R: Read>(&mut self, name: &str, r: &mut R) -> epee_encoding::error::Result<bool> {
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<NetworkAddress> {
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")),
},
)
}
}

195
net/monero-wire/src/p2p.rs Normal file
View file

@ -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<T: serde::de::DeserializeOwned, Ret>(
ret: impl FnOnce(T) -> Ret,
buf: &[u8],
) -> Result<Ret, BucketError> {
let t = monero_epee_bin_serde::from_bytes(buf)
.map_err(|e| BucketError::BodyDecodingError(e.into()))?;
Ok(ret(t))
}
fn build_message<T: serde::Serialize>(
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<Self, BucketError> {
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<Self, BucketError> {
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<Self, BucketError> {
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<Self, BucketError> {
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)
}
}
}
}

View file

@ -18,12 +18,12 @@
//! Admin message requests must be responded to in order unlike //! Admin message requests must be responded to in order unlike
//! protocol messages. //! protocol messages.
use epee_encoding::EpeeObject; use serde::{Deserialize, Serialize};
use super::common::{BasicNodeData, CoreSyncData, PeerListEntryBase, PeerSupportFlags}; use super::common::{BasicNodeData, CoreSyncData, PeerListEntryBase, PeerSupportFlags};
/// A Handshake Request /// A Handshake Request
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HandshakeRequest { pub struct HandshakeRequest {
/// Basic Node Data /// Basic Node Data
pub node_data: BasicNodeData, pub node_data: BasicNodeData,
@ -32,30 +32,31 @@ pub struct HandshakeRequest {
} }
/// A Handshake Response /// A Handshake Response
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HandshakeResponse { pub struct HandshakeResponse {
/// Basic Node Data /// Basic Node Data
pub node_data: BasicNodeData, pub node_data: BasicNodeData,
/// Core Sync Data /// Core Sync Data
pub payload_data: CoreSyncData, pub payload_data: CoreSyncData,
/// PeerList /// PeerList
#[epee_default(Vec::new())] #[serde(default = "Vec::new")]
pub local_peerlist_new: Vec<PeerListEntryBase>, pub local_peerlist_new: Vec<PeerListEntryBase>,
} }
/// A TimedSync Request /// A TimedSync Request
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TimedSyncRequest { pub struct TimedSyncRequest {
/// Core Sync Data /// Core Sync Data
pub payload_data: CoreSyncData, pub payload_data: CoreSyncData,
} }
/// A TimedSync Response /// A TimedSync Response
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TimedSyncResponse { pub struct TimedSyncResponse {
/// Core Sync Data /// Core Sync Data
pub payload_data: CoreSyncData, pub payload_data: CoreSyncData,
/// PeerList /// PeerList
#[serde(default = "Vec::new")]
pub local_peerlist_new: Vec<PeerListEntryBase>, pub local_peerlist_new: Vec<PeerListEntryBase>,
} }
@ -63,7 +64,7 @@ pub struct TimedSyncResponse {
pub const PING_OK_RESPONSE_STATUS_TEXT: &str = "OK"; pub const PING_OK_RESPONSE_STATUS_TEXT: &str = "OK";
/// A Ping Response /// A Ping Response
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PingResponse { pub struct PingResponse {
/// Status: should be `PING_OK_RESPONSE_STATUS_TEXT` /// Status: should be `PING_OK_RESPONSE_STATUS_TEXT`
pub status: String, pub status: String,
@ -72,10 +73,9 @@ pub struct PingResponse {
} }
/// A Support Flags Response /// A Support Flags Response
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SupportFlagsResponse { pub struct SupportFlagsResponse {
/// Support Flags /// Support Flags
#[epee_try_from_into(u32)]
pub support_flags: PeerSupportFlags, pub support_flags: PeerSupportFlags,
} }
@ -83,7 +83,7 @@ pub struct SupportFlagsResponse {
mod tests { mod tests {
use super::{BasicNodeData, CoreSyncData, HandshakeRequest, HandshakeResponse}; use super::{BasicNodeData, CoreSyncData, HandshakeRequest, HandshakeResponse};
use crate::messages::common::PeerSupportFlags; use crate::p2p::common::PeerSupportFlags;
#[test] #[test]
fn serde_handshake_req() { 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, 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, 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 { let basic_node_data = BasicNodeData {
my_port: 0, my_port: 0,
network_id: [ network_id: [
@ -128,8 +128,9 @@ mod tests {
assert_eq!(basic_node_data, handshake.node_data); assert_eq!(basic_node_data, handshake.node_data);
assert_eq!(core_sync_data, handshake.payload_data); assert_eq!(core_sync_data, handshake.payload_data);
let encoded_bytes = epee_encoding::to_bytes(&handshake).unwrap(); let encoded_bytes = monero_epee_bin_serde::to_bytes(&handshake).unwrap();
let handshake_2: HandshakeRequest = epee_encoding::from_bytes(&encoded_bytes).unwrap(); let handshake_2: HandshakeRequest =
monero_epee_bin_serde::from_bytes(&encoded_bytes).unwrap();
assert_eq!(handshake, handshake_2); 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, 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, 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 { let basic_node_data = BasicNodeData {
my_port: 18080, my_port: 18080,
@ -934,8 +935,9 @@ mod tests {
assert_eq!(core_sync_data, handshake.payload_data); assert_eq!(core_sync_data, handshake.payload_data);
assert_eq!(250, handshake.local_peerlist_new.len()); assert_eq!(250, handshake.local_peerlist_new.len());
let encoded_bytes = epee_encoding::to_bytes(&handshake).unwrap(); let encoded_bytes = monero_epee_bin_serde::to_bytes(&handshake).unwrap();
let handshake_2: HandshakeResponse = epee_encoding::from_bytes(&encoded_bytes).unwrap(); let handshake_2: HandshakeResponse =
monero_epee_bin_serde::from_bytes(&encoded_bytes).unwrap();
assert_eq!(handshake, handshake_2); assert_eq!(handshake, handshake_2);
} }

View file

@ -14,20 +14,17 @@
// //
//! Common types that are used across multiple messages. //! Common types that are used across multiple messages.
//
use epee_encoding::EpeeObject;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{serde_as, Bytes}; use serde_bytes::ByteBuf;
use crate::{ use crate::{
serde_helpers::{default_false, default_zero}, serde_helpers::{default_false, default_zero},
NetworkAddress, NetworkAddress,
}; };
mod builders; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
mod serde_impls; #[serde(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PeerSupportFlags(u32); pub struct PeerSupportFlags(u32);
impl From<u32> for PeerSupportFlags { impl From<u32> for PeerSupportFlags {
@ -70,7 +67,7 @@ impl From<u8> for PeerSupportFlags {
} }
/// Basic Node Data, information on the connected peer /// Basic Node Data, information on the connected peer
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BasicNodeData { pub struct BasicNodeData {
/// Port /// Port
pub my_port: u32, pub my_port: u32,
@ -80,39 +77,38 @@ pub struct BasicNodeData {
pub peer_id: u64, pub peer_id: u64,
/// The Peers Support Flags /// The Peers Support Flags
/// (If this is not in the message the default is 0) /// (If this is not in the message the default is 0)
#[epee_try_from_into(u32)] #[serde(default = "default_zero")]
#[epee_default(0_u32)]
pub support_flags: PeerSupportFlags, pub support_flags: PeerSupportFlags,
/// RPC Port /// RPC Port
/// (If this is not in the message the default is 0) /// (If this is not in the message the default is 0)
#[epee_default(0)] #[serde(default = "default_zero")]
pub rpc_port: u16, pub rpc_port: u16,
/// RPC Credits Per Hash /// RPC Credits Per Hash
/// (If this is not in the message the default is 0) /// (If this is not in the message the default is 0)
#[epee_default(0)] #[serde(default = "default_zero")]
pub rpc_credits_per_hash: u32, pub rpc_credits_per_hash: u32,
} }
/// Core Sync Data, information on the sync state of a peer /// 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 { pub struct CoreSyncData {
/// Cumulative Difficulty Low /// Cumulative Difficulty Low
/// The lower 64 bits of the 128 bit cumulative difficulty /// The lower 64 bits of the 128 bit cumulative difficulty
pub cumulative_difficulty: u64, pub cumulative_difficulty: u64,
/// Cumulative Difficulty High /// Cumulative Difficulty High
/// The upper 64 bits of the 128 bit cumulative difficulty /// The upper 64 bits of the 128 bit cumulative difficulty
#[epee_default(0)] #[serde(default = "default_zero")]
pub cumulative_difficulty_top64: u64, pub cumulative_difficulty_top64: u64,
/// Current Height of the peer /// Current Height of the peer
pub current_height: u64, pub current_height: u64,
/// Pruning Seed of the peer /// Pruning Seed of the peer
/// (If this is not in the message the default is 0) /// (If this is not in the message the default is 0)
#[epee_default(0)] #[serde(default = "default_zero")]
pub pruning_seed: u32, pub pruning_seed: u32,
/// Hash of the top block /// Hash of the top block
pub top_id: [u8; 32], pub top_id: [u8; 32],
/// Version of the top block /// Version of the top block
#[epee_default(0)] #[serde(default = "default_zero")]
pub top_version: u8, pub top_version: u8,
} }
@ -145,23 +141,23 @@ impl CoreSyncData {
/// PeerListEntryBase, information kept on a peer which will be entered /// PeerListEntryBase, information kept on a peer which will be entered
/// in a peer list/store. /// in a peer list/store.
#[derive(Clone, Copy, EpeeObject, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct PeerListEntryBase { pub struct PeerListEntryBase {
/// The Peer Address /// The Peer Address
pub adr: NetworkAddress, pub adr: NetworkAddress,
/// The Peer ID /// The Peer ID
pub id: u64, pub id: u64,
/// The last Time The Peer Was Seen /// The last Time The Peer Was Seen
#[epee_default(0)] #[serde(default = "default_zero")]
pub last_seen: i64, pub last_seen: i64,
/// The Pruning Seed /// The Pruning Seed
#[epee_default(0)] #[serde(default = "default_zero")]
pub pruning_seed: u32, pub pruning_seed: u32,
/// The RPC port /// The RPC port
#[epee_default(0)] #[serde(default = "default_zero")]
pub rpc_port: u16, pub rpc_port: u16,
/// The RPC credits per hash /// The RPC credits per hash
#[epee_default(0)] #[serde(default = "default_zero")]
pub rpc_credits_per_hash: u32, 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 /// 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 { pub struct PrunedTxBlobEntry {
/// The Tx /// The Tx
pub tx: Vec<u8>, pub tx: ByteBuf,
/// The Prunable Tx Hash /// The Prunable Tx Hash
pub prunable_hash: [u8; 32], pub prunable_hash: [u8; 32],
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum TransactionBlobs { pub enum TransactionBlobs {
Pruned(Vec<PrunedTxBlobEntry>), Pruned(Vec<PrunedTxBlobEntry>),
Normal(Vec<Vec<u8>>), Normal(Vec<ByteBuf>),
#[serde(skip_serializing)]
None,
} }
impl TransactionBlobs { impl TransactionBlobs {
@ -192,33 +191,36 @@ impl TransactionBlobs {
match self { match self {
TransactionBlobs::Normal(txs) => txs.len(), TransactionBlobs::Normal(txs) => txs.len(),
TransactionBlobs::Pruned(txs) => txs.len(), TransactionBlobs::Pruned(txs) => txs.len(),
TransactionBlobs::None => 0,
} }
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len() == 0 self.len() == 0
} }
fn none() -> TransactionBlobs {
TransactionBlobs::None
}
} }
/// A Block that can contain transactions /// A Block that can contain transactions
#[serde_as] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Clone, Debug, EpeeObject, Serialize, Deserialize, PartialEq, Eq)]
pub struct BlockCompleteEntry { pub struct BlockCompleteEntry {
/// True if tx data is pruned /// True if tx data is pruned
#[epee_default(false)]
#[serde(default = "default_false")] #[serde(default = "default_false")]
pub pruned: bool, pub pruned: bool,
/// The Block /// The Block
#[serde_as(as = "Bytes")] //#[serde_as(as = "Bytes")]
#[serde(with = "serde_bytes")]
pub block: Vec<u8>, pub block: Vec<u8>,
/// The Block Weight/Size /// The Block Weight/Size
#[epee_default(0)]
#[serde(default = "default_zero")] #[serde(default = "default_zero")]
pub block_weight: u64, pub block_weight: u64,
/// The blocks txs /// The blocks txs
#[epee_default(None)] #[serde(skip_serializing_if = "TransactionBlobs::is_empty")]
#[serde(skip_serializing_if = "Option::is_none")] #[serde(default = "TransactionBlobs::none")]
pub txs: Option<TransactionBlobs>, pub txs: TransactionBlobs,
} }
#[cfg(test)] #[cfg(test)]

View file

@ -18,12 +18,13 @@
//! Protocol message requests don't have to be responded to in order unlike //! Protocol message requests don't have to be responded to in order unlike
//! admin messages. //! admin messages.
use epee_encoding::EpeeObject; use serde::{Deserialize, Serialize};
use super::common::BlockCompleteEntry; use super::common::BlockCompleteEntry;
use crate::serde_helpers::*;
/// A block that SHOULD have transactions /// A block that SHOULD have transactions
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NewBlock { pub struct NewBlock {
/// Block with txs /// Block with txs
pub b: BlockCompleteEntry, pub b: BlockCompleteEntry,
@ -32,51 +33,53 @@ pub struct NewBlock {
} }
/// New Tx Pool Transactions /// New Tx Pool Transactions
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NewTransactions { pub struct NewTransactions {
/// Tx Blobs /// Tx Blobs
pub txs: Vec<Vec<u8>>, pub txs: Vec<Vec<u8>>,
/// Dandelionpp true if fluff - backwards compatible mode is fluff /// Dandelionpp true if fluff - backwards compatible mode is fluff
#[epee_default(true)] #[serde(default = "default_true")]
pub dandelionpp_fluff: bool, pub dandelionpp_fluff: bool,
/// Padding /// Padding
#[epee_alt_name("_")] #[serde(rename = "_")]
pub padding: Vec<u8>, pub padding: Vec<u8>,
} }
/// A Request For Blocks /// A Request For Blocks
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GetObjectsRequest { pub struct GetObjectsRequest {
/// Block hashes we want /// Block hashes we want
pub blocks: Vec<[u8; 32]>, pub blocks: Vec<[u8; 32]>,
/// Pruned /// Pruned
#[epee_default(false)] #[serde(default = "default_false")]
pub pruned: bool, pub pruned: bool,
} }
/// A Blocks Response /// A Blocks Response
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GetObjectsResponse { pub struct GetObjectsResponse {
/// Blocks /// Blocks
// We dont need to give this a default value as there always is at least 1 block
pub blocks: Vec<BlockCompleteEntry>, pub blocks: Vec<BlockCompleteEntry>,
/// Missed IDs /// Missed IDs
#[serde(default = "Vec::new")]
pub missed_ids: Vec<[u8; 32]>, pub missed_ids: Vec<[u8; 32]>,
/// The height of the peers blockchain /// The height of the peers blockchain
pub current_blockchain_height: u64, pub current_blockchain_height: u64,
} }
/// A Chain Request /// A Chain Request
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ChainRequest { pub struct ChainRequest {
/// Block IDs /// Block IDs
pub block_ids: Vec<[u8; 32]>, pub block_ids: Vec<[u8; 32]>,
/// Prune /// Prune
#[epee_default(false)] #[serde(default = "default_false")]
pub prune: bool, pub prune: bool,
} }
/// A Chain Response /// A Chain Response
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ChainResponse { pub struct ChainResponse {
/// Start Height /// Start Height
pub start_height: u64, pub start_height: u64,
@ -123,7 +126,7 @@ impl ChainResponse {
} }
/// A Block that doesn't have transactions unless requested /// 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 { pub struct NewFluffyBlock {
/// Block which might have transactions /// Block which might have transactions
pub b: BlockCompleteEntry, pub b: BlockCompleteEntry,
@ -132,7 +135,7 @@ pub struct NewFluffyBlock {
} }
/// A request for Txs we are missing from our TxPool /// 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 { pub struct FluffyMissingTransactionsRequest {
/// The Block we are missing the Txs in /// The Block we are missing the Txs in
pub block_hash: [u8; 32], pub block_hash: [u8; 32],
@ -143,9 +146,10 @@ pub struct FluffyMissingTransactionsRequest {
} }
/// TxPoolCompliment /// TxPoolCompliment
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GetTxPoolCompliment { pub struct GetTxPoolCompliment {
/// Tx Hashes /// Tx Hashes
#[serde(default = "Vec::new")]
pub hashes: Vec<[u8; 32]>, pub hashes: Vec<[u8; 32]>,
} }