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