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:
Boog900 2023-07-17 17:43:34 +00:00 committed by GitHub
parent e6e8bdaf6c
commit 477d9e42f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 588 additions and 1036 deletions

View file

@ -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"
] ]

View file

@ -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>;

View file

@ -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"

View file

@ -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;
}
};
}

View file

@ -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>;

View file

@ -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)?),
#[sealed::sealed] 2007 => ProtocolMessage::ChainEntryResponse(epee_encoding::from_bytes(buf)?),
pub trait ProtocolMessage { 2008 => ProtocolMessage::NewFluffyBlock(epee_encoding::from_bytes(buf)?),
const ID: u32; 2009 => {
const NAME: &'static str; ProtocolMessage::FluffyMissingTransactionsRequest(epee_encoding::from_bytes(buf)?)
type Notification: NetworkMessage;
}
macro_rules! p2p_command {
($($message:ident),+) => {
pub enum P2pCommand {
$($message,)+
} }
2010 => ProtocolMessage::GetTxPoolCompliment(epee_encoding::from_bytes(buf)?),
pub struct P2pCommandFromU32Err; _ => {
impl TryFrom<u32> for P2pCommand { return Err(epee_encoding::Error::Value(
type Error = P2pCommandFromU32Err; "Failed to decode message, unknown command",
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> { fn build(&self, builder: &mut BucketBuilder) -> Result<(), epee_encoding::Error> {
match self { match self {
Message::Response(mes) => { ProtocolMessage::NewBlock(nb) => {
let (command, bytes)= mes.encode()?; builder.set_command(2001);
Ok((1, command, MessageType::Response, bytes)) builder.set_body(epee_encoding::to_bytes(nb)?);
}, }
Message::Request(mes) => { ProtocolMessage::NewTransactions(nt) => {
let (command, bytes)= mes.encode()?; builder.set_command(2002);
Ok((0, command, MessageType::Request, bytes)) builder.set_body(epee_encoding::to_bytes(nt)?);
}, }
Message::Notification(mes) => { ProtocolMessage::GetObjectsRequest(gt) => {
let (command, bytes)= mes.encode()?; builder.set_command(2003);
Ok((0, command, MessageType::Notification, bytes)) 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(())
} }
};
} }
p2p_command!( pub enum RequestMessage {
Handshake, Handshake(HandshakeRequest),
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(())
}
}

View file

@ -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,9 +118,7 @@ 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() .unwrap()
.try_into() .try_into()
.unwrap(), .unwrap(),
@ -229,8 +128,8 @@ mod tests {
assert_eq!(basic_node_data, handshake.node_data); assert_eq!(basic_node_data, handshake.node_data);
assert_eq!(core_sync_data, handshake.payload_data); assert_eq!(core_sync_data, handshake.payload_data);
let encoded_bytes = epee_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,9 +923,7 @@ 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() .unwrap()
.try_into() .try_into()
.unwrap(), .unwrap(),
@ -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);
} }

View file

@ -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)]

View 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)),
}
}
}

View file

@ -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);
} }

View file

@ -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()
}
}

View 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")),
},
)
}
}

View file

@ -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)
}