mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-12-22 19:49:28 +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]
|
[workspace]
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
"common",
|
# "common",
|
||||||
"cuprate",
|
#"cuprate",
|
||||||
"database",
|
# "database",
|
||||||
"net/levin",
|
"net/levin",
|
||||||
"net/monero-wire",
|
"net/monero-wire",
|
||||||
"p2p",
|
# "p2p",
|
||||||
# "p2p/sync-states"
|
# "p2p/sync-states"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,9 @@ pub enum BucketError {
|
||||||
/// Invalid Fragmented Message
|
/// Invalid Fragmented Message
|
||||||
#[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("Error decoding bucket body")]
|
||||||
|
BodyDecodingError(Box<dyn Debug>),
|
||||||
/// 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),
|
||||||
|
@ -73,7 +76,7 @@ pub struct Bucket {
|
||||||
pub body: Vec<u8>,
|
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)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum MessageType {
|
pub enum MessageType {
|
||||||
/// Request
|
/// Request
|
||||||
|
@ -125,6 +128,7 @@ impl MessageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct BucketBuilder {
|
pub struct BucketBuilder {
|
||||||
signature: Option<u64>,
|
signature: Option<u64>,
|
||||||
ty: Option<MessageType>,
|
ty: Option<MessageType>,
|
||||||
|
@ -193,7 +197,7 @@ impl BucketBuilder {
|
||||||
/// A levin body
|
/// A levin body
|
||||||
pub trait LevinBody: Sized {
|
pub trait LevinBody: Sized {
|
||||||
/// Decodes the message from the data in the header
|
/// 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
|
/// Encodes the message
|
||||||
fn encode(&self, builder: &mut BucketBuilder) -> Result<(), BucketError>;
|
fn encode(&self, builder: &mut BucketBuilder) -> Result<(), BucketError>;
|
||||||
|
|
|
@ -8,14 +8,9 @@ repository = "https://github.com/SyntheticBird45/cuprate/tree/main/net/monero-wi
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
levin = {path="../levin"}
|
levin-cuprate = {path="../levin"}
|
||||||
serde = {version = "1.0", features =["derive"]}
|
epee-encoding = { git = "https://github.com/boog900/epee-encoding"}
|
||||||
serde_with = "2.2.0"
|
|
||||||
sealed = "0.4"
|
[dev-dependencies]
|
||||||
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"
|
|
||||||
hex = "0.4.3"
|
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
|
//! # Monero Wire
|
||||||
//!
|
//!
|
||||||
//! A crate defining Monero network messages and network addresses,
|
//! 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
|
//! ## License
|
||||||
//!
|
//!
|
||||||
|
@ -29,13 +29,11 @@
|
||||||
#![deny(unused_mut)]
|
#![deny(unused_mut)]
|
||||||
//#![deny(missing_docs)]
|
//#![deny(missing_docs)]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod internal_macros;
|
|
||||||
pub mod messages;
|
pub mod messages;
|
||||||
pub mod network_address;
|
pub mod network_address;
|
||||||
mod utils;
|
|
||||||
|
|
||||||
pub use messages::{Message, P2pCommand};
|
|
||||||
pub use network_address::NetworkAddress;
|
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
|
//! This module defines a Monero `Message` enum which contains
|
||||||
//! every possible Monero network message (levin body)
|
//! every possible Monero network message (levin body)
|
||||||
|
|
||||||
|
use levin_cuprate::{BucketBuilder, BucketError, LevinBody, MessageType};
|
||||||
|
|
||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
|
|
||||||
pub use admin::{Handshake, Ping, SupportFlags, TimedSync};
|
pub use admin::{
|
||||||
pub use common::{BasicNodeData, CoreSyncData, PeerID, PeerListEntryBase};
|
HandshakeRequest, HandshakeResponse, PingResponse, SupportFlagsResponse, TimedSyncRequest,
|
||||||
|
TimedSyncResponse,
|
||||||
|
};
|
||||||
|
pub use common::{BasicNodeData, CoreSyncData, PeerListEntryBase};
|
||||||
pub use protocol::{
|
pub use protocol::{
|
||||||
ChainRequest, ChainResponse, FluffyMissingTransactionsRequest, GetObjectsRequest,
|
ChainRequest, ChainResponse, FluffyMissingTransactionsRequest, GetObjectsRequest,
|
||||||
GetObjectsResponse, GetTxPoolCompliment, NewBlock, NewFluffyBlock, NewTransactions,
|
GetObjectsResponse, GetTxPoolCompliment, NewBlock, NewFluffyBlock, NewTransactions,
|
||||||
};
|
};
|
||||||
|
|
||||||
use levin::{BucketError, MessageType};
|
pub enum ProtocolMessage {
|
||||||
|
NewBlock(NewBlock),
|
||||||
#[sealed::sealed]
|
NewFluffyBlock(NewFluffyBlock),
|
||||||
pub trait NetworkMessage: Sized {
|
GetObjectsRequest(GetObjectsRequest),
|
||||||
type EncodingError: std::fmt::Debug;
|
GetObjectsResponse(GetObjectsResponse),
|
||||||
fn decode(buf: &[u8]) -> Result<Self, Self::EncodingError>;
|
ChainRequest(ChainRequest),
|
||||||
fn encode(&self) -> Result<Vec<u8>, Self::EncodingError>;
|
ChainEntryResponse(ChainResponse),
|
||||||
|
NewTransactions(NewTransactions),
|
||||||
|
FluffyMissingTransactionsRequest(FluffyMissingTransactionsRequest),
|
||||||
|
GetTxPoolCompliment(GetTxPoolCompliment),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[sealed::sealed]
|
impl ProtocolMessage {
|
||||||
pub trait AdminMessage {
|
fn decode(buf: &[u8], command: u32) -> Result<Self, epee_encoding::Error> {
|
||||||
const ID: u32;
|
Ok(match command {
|
||||||
const NAME: &'static str;
|
2001 => ProtocolMessage::NewBlock(epee_encoding::from_bytes(buf)?),
|
||||||
type Request: NetworkMessage;
|
2002 => ProtocolMessage::NewTransactions(epee_encoding::from_bytes(buf)?),
|
||||||
type Response: NetworkMessage;
|
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 enum RequestMessage {
|
||||||
pub trait ProtocolMessage {
|
Handshake(HandshakeRequest),
|
||||||
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,
|
|
||||||
Ping,
|
Ping,
|
||||||
SupportFlags,
|
SupportFlags,
|
||||||
NewBlock,
|
TimedSync(TimedSyncRequest),
|
||||||
NewTransactions,
|
}
|
||||||
GetObjectsRequest,
|
|
||||||
GetObjectsResponse,
|
|
||||||
ChainRequest,
|
|
||||||
ChainResponse,
|
|
||||||
NewFluffyBlock,
|
|
||||||
FluffyMissingTransactionsRequest,
|
|
||||||
GetTxPoolCompliment
|
|
||||||
);
|
|
||||||
|
|
||||||
levin_body!(
|
impl RequestMessage {
|
||||||
Admin:
|
fn decode(buf: &[u8], command: u32) -> Result<Self, epee_encoding::Error> {
|
||||||
Handshake,
|
Ok(match command {
|
||||||
TimedSync,
|
1001 => RequestMessage::Handshake(epee_encoding::from_bytes(buf)?),
|
||||||
Ping,
|
1002 => RequestMessage::TimedSync(epee_encoding::from_bytes(buf)?),
|
||||||
SupportFlags
|
1003 => RequestMessage::Ping,
|
||||||
Protocol:
|
1007 => RequestMessage::SupportFlags,
|
||||||
NewBlock,
|
_ => {
|
||||||
NewTransactions,
|
return Err(epee_encoding::Error::Value(
|
||||||
GetObjectsRequest,
|
"Failed to decode message, unknown command",
|
||||||
GetObjectsResponse,
|
))
|
||||||
ChainRequest,
|
}
|
||||||
ChainResponse,
|
})
|
||||||
NewFluffyBlock,
|
}
|
||||||
FluffyMissingTransactionsRequest,
|
|
||||||
GetTxPoolCompliment
|
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
|
//! Admin message requests must be responded to in order unlike
|
||||||
//! protocol messages.
|
//! protocol messages.
|
||||||
|
|
||||||
use std::fmt::Display;
|
use epee_encoding::EpeeObject;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use super::common::{BasicNodeData, CoreSyncData, PeerListEntryBase, PeerSupportFlags};
|
||||||
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Handshake Request
|
/// A Handshake Request
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
pub struct HandshakeRequest {
|
pub struct HandshakeRequest {
|
||||||
/// Basic Node Data
|
/// Basic Node Data
|
||||||
pub node_data: BasicNodeData,
|
pub node_data: BasicNodeData,
|
||||||
|
@ -55,47 +31,27 @@ pub struct HandshakeRequest {
|
||||||
pub payload_data: CoreSyncData,
|
pub payload_data: CoreSyncData,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty_vec<T>() -> Vec<T> {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Handshake Response
|
/// A Handshake Response
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
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
|
||||||
#[serde(default = "empty_vec")]
|
#[epee_default(Vec::new())]
|
||||||
pub local_peerlist_new: Vec<PeerListEntryBase>,
|
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
|
/// A TimedSync Request
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
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, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
pub struct TimedSyncResponse {
|
pub struct TimedSyncResponse {
|
||||||
/// Core Sync Data
|
/// Core Sync Data
|
||||||
pub payload_data: CoreSyncData,
|
pub payload_data: CoreSyncData,
|
||||||
|
@ -103,86 +59,31 @@ pub struct TimedSyncResponse {
|
||||||
pub local_peerlist_new: Vec<PeerListEntryBase>,
|
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
|
/// The status field of an okay ping response
|
||||||
pub const PING_OK_RESPONSE_STATUS_TEXT: &str = "OK";
|
pub const PING_OK_RESPONSE_STATUS_TEXT: &str = "OK";
|
||||||
|
|
||||||
/// A Ping Request
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
|
||||||
pub struct PingRequest;
|
|
||||||
|
|
||||||
/// A Ping Response
|
/// A Ping Response
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
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,
|
||||||
/// Peer ID
|
/// 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
|
/// A Support Flags Response
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
pub struct SupportFlagsResponse {
|
pub struct SupportFlagsResponse {
|
||||||
/// Support Flags
|
/// Support Flags
|
||||||
|
#[epee_try_from_into(u32)]
|
||||||
pub support_flags: PeerSupportFlags,
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::{BasicNodeData, CoreSyncData, HandshakeRequest, HandshakeResponse};
|
use super::{BasicNodeData, CoreSyncData, HandshakeRequest, HandshakeResponse};
|
||||||
use crate::messages::common::{PeerID, PeerSupportFlags};
|
use crate::messages::common::PeerSupportFlags;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serde_handshake_req() {
|
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,
|
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_serde::from_bytes(bytes).unwrap();
|
let handshake: HandshakeRequest = epee_encoding::from_bytes(&bytes).unwrap();
|
||||||
let basic_node_data = BasicNodeData {
|
let basic_node_data = BasicNodeData {
|
||||||
my_port: 0,
|
my_port: 0,
|
||||||
network_id: [
|
network_id: [
|
||||||
18, 48, 241, 113, 97, 4, 65, 97, 23, 49, 0, 130, 22, 161, 161, 16,
|
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),
|
support_flags: PeerSupportFlags::from(1_u32),
|
||||||
rpc_port: 0,
|
rpc_port: 0,
|
||||||
rpc_credits_per_hash: 0,
|
rpc_credits_per_hash: 0,
|
||||||
|
@ -217,20 +118,18 @@ mod tests {
|
||||||
cumulative_difficulty_top64: 0,
|
cumulative_difficulty_top64: 0,
|
||||||
current_height: 0,
|
current_height: 0,
|
||||||
pruning_seed: 0,
|
pruning_seed: 0,
|
||||||
top_id: hex::decode(
|
top_id: hex::decode("418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3")
|
||||||
"0x418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3",
|
.unwrap()
|
||||||
)
|
.try_into()
|
||||||
.unwrap()
|
.unwrap(),
|
||||||
.try_into()
|
|
||||||
.unwrap(),
|
|
||||||
top_version: 1,
|
top_version: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
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_serde::to_bytes(&handshake).unwrap();
|
let encoded_bytes = epee_encoding::to_bytes(&handshake).unwrap();
|
||||||
let handshake_2: HandshakeRequest = epee_serde::from_bytes(encoded_bytes).unwrap();
|
let handshake_2: HandshakeRequest = epee_encoding::from_bytes(&encoded_bytes).unwrap();
|
||||||
|
|
||||||
assert_eq!(handshake, handshake_2);
|
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,
|
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_serde::from_bytes(bytes).unwrap();
|
let handshake: HandshakeResponse = epee_encoding::from_bytes(&bytes).unwrap();
|
||||||
|
|
||||||
let basic_node_data = BasicNodeData {
|
let basic_node_data = BasicNodeData {
|
||||||
my_port: 18080,
|
my_port: 18080,
|
||||||
network_id: [
|
network_id: [
|
||||||
18, 48, 241, 113, 97, 4, 65, 97, 23, 49, 0, 130, 22, 161, 161, 16,
|
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),
|
support_flags: PeerSupportFlags::from(1_u32),
|
||||||
rpc_port: 18089,
|
rpc_port: 18089,
|
||||||
rpc_credits_per_hash: 0,
|
rpc_credits_per_hash: 0,
|
||||||
|
@ -1024,12 +923,10 @@ mod tests {
|
||||||
cumulative_difficulty_top64: 0,
|
cumulative_difficulty_top64: 0,
|
||||||
current_height: 2775167,
|
current_height: 2775167,
|
||||||
pruning_seed: 386,
|
pruning_seed: 386,
|
||||||
top_id: hex::decode(
|
top_id: hex::decode("40780072dae9123108599a9f6585f2474d03f7b6dbb5d8c18717baa8cf7756eb")
|
||||||
"0x40780072dae9123108599a9f6585f2474d03f7b6dbb5d8c18717baa8cf7756eb",
|
.unwrap()
|
||||||
)
|
.try_into()
|
||||||
.unwrap()
|
.unwrap(),
|
||||||
.try_into()
|
|
||||||
.unwrap(),
|
|
||||||
top_version: 16,
|
top_version: 16,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1037,8 +934,8 @@ 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_serde::to_bytes(&handshake).unwrap();
|
let encoded_bytes = epee_encoding::to_bytes(&handshake).unwrap();
|
||||||
let handshake_2: HandshakeResponse = epee_serde::from_bytes(encoded_bytes).unwrap();
|
let handshake_2: HandshakeResponse = epee_encoding::from_bytes(&encoded_bytes).unwrap();
|
||||||
|
|
||||||
assert_eq!(handshake, handshake_2);
|
assert_eq!(handshake, handshake_2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,44 +15,14 @@
|
||||||
|
|
||||||
//! Common types that are used across multiple messages.
|
//! 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;
|
use crate::NetworkAddress;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
|
mod builders;
|
||||||
#[serde(transparent)]
|
|
||||||
pub struct PeerSupportFlags(u32); // had to name it this to avoid conflict
|
|
||||||
|
|
||||||
impl PeerSupportFlags {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
const FLUFFY_BLOCKS: u32 = 0b0000_0001;
|
pub struct PeerSupportFlags(u32);
|
||||||
/// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u32> for PeerSupportFlags {
|
impl From<u32> for PeerSupportFlags {
|
||||||
fn from(value: u32) -> Self {
|
fn from(value: u32) -> Self {
|
||||||
|
@ -60,56 +30,83 @@ impl From<u32> for PeerSupportFlags {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A PeerID, different from a `NetworkAddress`
|
impl From<PeerSupportFlags> for u32 {
|
||||||
#[derive(Debug, Clone, Default, Copy, Deserialize, Serialize, PartialEq, Eq)]
|
fn from(value: PeerSupportFlags) -> Self {
|
||||||
#[serde(transparent)]
|
value.0
|
||||||
pub struct PeerID(pub u64);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
/// Basic Node Data, information on the connected peer
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
pub struct BasicNodeData {
|
pub struct BasicNodeData {
|
||||||
/// Port
|
/// Port
|
||||||
pub my_port: u32,
|
pub my_port: u32,
|
||||||
/// The Network Id
|
/// The Network Id
|
||||||
pub network_id: [u8; 16],
|
pub network_id: [u8; 16],
|
||||||
/// Peer ID
|
/// Peer ID
|
||||||
pub peer_id: PeerID,
|
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)
|
||||||
#[serde(default = "utils::zero_val")]
|
#[epee_try_from_into(u32)]
|
||||||
|
#[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)
|
||||||
#[serde(default = "utils::zero_val")]
|
#[epee_default(0)]
|
||||||
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)
|
||||||
#[serde(default = "utils::zero_val")]
|
#[epee_default(0)]
|
||||||
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
|
||||||
#[serde_as]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
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
|
||||||
#[serde(default = "utils::zero_val")]
|
#[epee_default(0)]
|
||||||
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)
|
||||||
#[serde(default = "utils::zero_val")]
|
#[epee_default(0)]
|
||||||
pub pruning_seed: u32,
|
pub pruning_seed: u32,
|
||||||
/// Hash of the top block
|
/// Hash of the top block
|
||||||
#[serde_as(as = "TryFromInto<[u8; 32]>")]
|
|
||||||
pub top_id: [u8; 32],
|
pub top_id: [u8; 32],
|
||||||
/// Version of the top block
|
/// Version of the top block
|
||||||
#[serde(default = "utils::zero_val")]
|
#[epee_default(0)]
|
||||||
pub top_version: u8,
|
pub top_version: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,31 +131,31 @@ impl CoreSyncData {
|
||||||
}
|
}
|
||||||
/// Returns the 128 bit cumulative difficulty of the peers blockchain
|
/// Returns the 128 bit cumulative difficulty of the peers blockchain
|
||||||
pub fn cumulative_difficulty(&self) -> u128 {
|
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 <<= 64;
|
||||||
ret | self.cumulative_difficulty as u128
|
ret | (Into::<u128>::into(self.cumulative_difficulty))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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, Default, Deserialize, Serialize, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, EpeeObject, Debug, Eq, PartialEq)]
|
||||||
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: PeerID,
|
pub id: u64,
|
||||||
/// The last Time The Peer Was Seen
|
/// The last Time The Peer Was Seen
|
||||||
#[serde(default = "utils::zero_val")]
|
#[epee_default(0)]
|
||||||
pub last_seen: i64,
|
pub last_seen: i64,
|
||||||
/// The Pruning Seed
|
/// The Pruning Seed
|
||||||
#[serde(default = "utils::zero_val")]
|
#[epee_default(0)]
|
||||||
pub pruning_seed: u32,
|
pub pruning_seed: u32,
|
||||||
/// The RPC port
|
/// The RPC port
|
||||||
#[serde(default = "utils::zero_val")]
|
#[epee_default(0)]
|
||||||
pub rpc_port: u16,
|
pub rpc_port: u16,
|
||||||
/// The RPC credits per hash
|
/// The RPC credits per hash
|
||||||
#[serde(default = "utils::zero_val")]
|
#[epee_default(0)]
|
||||||
pub rpc_credits_per_hash: u32,
|
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
|
/// 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 {
|
pub struct PrunedTxBlobEntry {
|
||||||
/// The Tx
|
/// The Tx
|
||||||
pub tx: Vec<u8>,
|
pub tx: Vec<u8>,
|
||||||
|
@ -178,39 +175,6 @@ pub struct PrunedTxBlobEntry {
|
||||||
pub prunable_hash: [u8; 32],
|
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)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum TransactionBlobs {
|
pub enum TransactionBlobs {
|
||||||
Pruned(Vec<PrunedTxBlobEntry>),
|
Pruned(Vec<PrunedTxBlobEntry>),
|
||||||
|
@ -228,138 +192,22 @@ impl TransactionBlobs {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
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
|
/// A Block that can contain transactions
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, EpeeObject, PartialEq, Eq)]
|
||||||
pub struct BlockCompleteEntry {
|
pub struct BlockCompleteEntry {
|
||||||
/// True if tx data is pruned
|
/// True if tx data is pruned
|
||||||
|
#[epee_default(false)]
|
||||||
pub pruned: bool,
|
pub pruned: bool,
|
||||||
/// The Block
|
/// The Block
|
||||||
pub block: Vec<u8>,
|
pub block: Vec<u8>,
|
||||||
/// The Block Weight/Size
|
/// The Block Weight/Size
|
||||||
|
#[epee_default(0)]
|
||||||
pub block_weight: u64,
|
pub block_weight: u64,
|
||||||
/// The blocks txs
|
/// The blocks txs
|
||||||
pub txs: TransactionBlobs,
|
#[epee_default(None)]
|
||||||
}
|
pub txs: Option<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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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
|
//! Protocol message requests don't have to be responded to in order unlike
|
||||||
//! admin messages.
|
//! admin messages.
|
||||||
|
|
||||||
use serde::Deserialize;
|
use epee_encoding::EpeeObject;
|
||||||
use serde::Serialize;
|
|
||||||
use serde_with::serde_as;
|
|
||||||
use serde_with::Bytes;
|
|
||||||
|
|
||||||
use super::common::BlockCompleteEntry;
|
use super::common::BlockCompleteEntry;
|
||||||
use crate::utils::{default_false, default_true};
|
|
||||||
|
|
||||||
const P2P_PROTOCOL_BASE: u32 = 2000;
|
|
||||||
|
|
||||||
/// A block that SHOULD have transactions
|
/// A block that SHOULD have transactions
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
pub struct NewBlock {
|
pub struct NewBlock {
|
||||||
/// Block with txs
|
/// Block with txs
|
||||||
pub b: BlockCompleteEntry,
|
pub b: BlockCompleteEntry,
|
||||||
|
@ -37,71 +31,31 @@ pub struct NewBlock {
|
||||||
pub current_blockchain_height: u64,
|
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
|
/// New Tx Pool Transactions
|
||||||
#[serde_as]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct NewTransactions {
|
pub struct NewTransactions {
|
||||||
/// Tx Blobs
|
/// Tx Blobs
|
||||||
pub txs: Vec<TxBlob>,
|
pub txs: Vec<Vec<u8>>,
|
||||||
/// Dandelionpp true if fluff - backwards compatible mode is fluff
|
/// Dandelionpp true if fluff - backwards compatible mode is fluff
|
||||||
#[serde(default = "default_true")]
|
#[epee_default(true)]
|
||||||
pub dandelionpp_fluff: bool,
|
pub dandelionpp_fluff: bool,
|
||||||
/// Padding
|
/// Padding
|
||||||
#[serde(rename = "_")]
|
#[epee_alt_name("_")]
|
||||||
#[serde_as(as = "Bytes")]
|
|
||||||
pub padding: Vec<u8>,
|
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
|
/// A Request For Blocks
|
||||||
#[serde_as]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct GetObjectsRequest {
|
pub struct GetObjectsRequest {
|
||||||
/// Blocks
|
/// Block hashes we want
|
||||||
pub blocks: Vec<[u8; 32]>,
|
pub blocks: Vec<[u8; 32]>,
|
||||||
/// Pruned
|
/// Pruned
|
||||||
#[serde(default = "default_false")]
|
#[epee_default(false)]
|
||||||
pub pruned: bool,
|
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
|
/// A Blocks Response
|
||||||
#[serde_as]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct GetObjectsResponse {
|
pub struct GetObjectsResponse {
|
||||||
/// Blocks
|
/// Blocks
|
||||||
pub blocks: Vec<BlockCompleteEntry>,
|
pub blocks: Vec<BlockCompleteEntry>,
|
||||||
|
@ -111,40 +65,18 @@ pub struct GetObjectsResponse {
|
||||||
pub current_blockchain_height: u64,
|
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
|
/// A Chain Request
|
||||||
#[serde_as]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct ChainRequest {
|
pub struct ChainRequest {
|
||||||
/// Block IDs
|
/// Block IDs
|
||||||
pub block_ids: Vec<[u8; 32]>,
|
pub block_ids: Vec<[u8; 32]>,
|
||||||
/// Prune
|
/// Prune
|
||||||
#[serde(default = "default_false")]
|
#[epee_default(false)]
|
||||||
pub prune: bool,
|
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
|
/// A Chain Response
|
||||||
#[serde_as]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct ChainResponse {
|
pub struct ChainResponse {
|
||||||
/// Start Height
|
/// Start Height
|
||||||
pub start_height: u64,
|
pub start_height: u64,
|
||||||
|
@ -159,7 +91,6 @@ pub struct ChainResponse {
|
||||||
/// Block Weights
|
/// Block Weights
|
||||||
pub m_block_weights: Vec<u64>,
|
pub m_block_weights: Vec<u64>,
|
||||||
/// The first Block in the response
|
/// The first Block in the response
|
||||||
#[serde_as(as = "Bytes")]
|
|
||||||
pub first_block: Vec<u8>,
|
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
|
/// 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 {
|
pub struct NewFluffyBlock {
|
||||||
/// Block which might have transactions
|
/// Block which might have transactions
|
||||||
pub b: BlockCompleteEntry,
|
pub b: BlockCompleteEntry,
|
||||||
|
@ -210,19 +131,8 @@ pub struct NewFluffyBlock {
|
||||||
pub current_blockchain_height: u64,
|
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
|
/// A request for Txs we are missing from our TxPool
|
||||||
#[serde_as]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
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],
|
||||||
|
@ -232,34 +142,13 @@ pub struct FluffyMissingTransactionsRequest {
|
||||||
pub missing_tx_indices: Vec<u64>,
|
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
|
/// TxPoolCompliment
|
||||||
#[serde_as]
|
#[derive(Debug, Clone, EpeeObject, PartialEq, Eq)]
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct GetTxPoolCompliment {
|
pub struct GetTxPoolCompliment {
|
||||||
/// Tx Hashes
|
/// Tx Hashes
|
||||||
pub hashes: Vec<[u8; 32]>,
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
@ -778,16 +667,17 @@ mod tests {
|
||||||
|
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
for _ in 0..1000 {
|
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());
|
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());
|
assert_eq!(4, new_transactions.txs.len());
|
||||||
|
|
||||||
let encoded_bytes = epee_serde::to_bytes(&new_transactions).unwrap();
|
let encoded_bytes = epee_encoding::to_bytes(&new_transactions).unwrap();
|
||||||
let new_transactions_2: NewTransactions = epee_serde::from_bytes(encoded_bytes).unwrap();
|
let new_transactions_2: NewTransactions =
|
||||||
|
epee_encoding::from_bytes(&encoded_bytes).unwrap();
|
||||||
|
|
||||||
assert_eq!(new_transactions, new_transactions_2);
|
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,
|
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,
|
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 encoded_bytes = epee_encoding::to_bytes(&fluffy_block).unwrap();
|
||||||
let fluffy_block_2: NewFluffyBlock = epee_serde::from_bytes(encoded_bytes).unwrap();
|
let fluffy_block_2: NewFluffyBlock = epee_encoding::from_bytes(&encoded_bytes).unwrap();
|
||||||
|
|
||||||
assert_eq!(fluffy_block, fluffy_block_2);
|
assert_eq!(fluffy_block, fluffy_block_2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,10 @@
|
||||||
//! Monero network. Core Monero has 4 main addresses: IPv4, IPv6, Tor,
|
//! Monero network. Core Monero has 4 main addresses: IPv4, IPv6, Tor,
|
||||||
//! I2p. Currently this module only has IPv(4/6).
|
//! I2p. Currently this module only has IPv(4/6).
|
||||||
//!
|
//!
|
||||||
|
use std::net::{SocketAddrV4, SocketAddrV6};
|
||||||
use std::{hash::Hash, net};
|
use std::{hash::Hash, net};
|
||||||
|
|
||||||
use epee_serde::Value;
|
mod builder;
|
||||||
use serde::{de, ser::SerializeStruct, Deserialize, Serialize};
|
|
||||||
|
|
||||||
use super::utils;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub enum NetZone {
|
pub enum NetZone {
|
||||||
|
@ -31,80 +29,14 @@ pub enum NetZone {
|
||||||
I2p,
|
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
|
/// 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)]
|
||||||
pub enum NetworkAddress {
|
pub enum NetworkAddress {
|
||||||
/// IPv4
|
/// IPv4
|
||||||
IPv4(IPv4Address),
|
IPv4(SocketAddrV4),
|
||||||
/// IPv6
|
/// IPv6
|
||||||
IPv6(IPv6Address),
|
IPv6(SocketAddrV6),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkAddress {
|
impl NetworkAddress {
|
||||||
|
@ -126,27 +58,21 @@ impl NetworkAddress {
|
||||||
|
|
||||||
pub fn port(&self) -> u16 {
|
pub fn port(&self) -> u16 {
|
||||||
match self {
|
match self {
|
||||||
NetworkAddress::IPv4(ip) => ip.m_port,
|
NetworkAddress::IPv4(ip) => ip.port(),
|
||||||
NetworkAddress::IPv6(ip) => ip.m_port,
|
NetworkAddress::IPv6(ip) => ip.port(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NetworkAddress {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::IPv4(IPv4Address::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<net::SocketAddrV4> for NetworkAddress {
|
impl From<net::SocketAddrV4> for NetworkAddress {
|
||||||
fn from(value: net::SocketAddrV4) -> Self {
|
fn from(value: net::SocketAddrV4) -> Self {
|
||||||
NetworkAddress::IPv4(value.into())
|
NetworkAddress::IPv4(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<net::SocketAddrV6> for NetworkAddress {
|
impl From<net::SocketAddrV6> for NetworkAddress {
|
||||||
fn from(value: net::SocketAddrV6) -> Self {
|
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