mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-11-16 15:58:17 +00:00
net: use epee_encoding instead of monero-epee-bin-serde
This gives us more control than what serde provides. This PR also moves to use `Bytes` where possible to allow zero-copy parsing of network messages.
This commit is contained in:
parent
2b65be4b18
commit
83b59c557c
30 changed files with 709 additions and 338 deletions
48
Cargo.lock
generated
48
Cargo.lock
generated
|
@ -438,10 +438,10 @@ dependencies = [
|
|||
"curve25519-dalek",
|
||||
"dalek-ff-group",
|
||||
"dirs",
|
||||
"epee-encoding",
|
||||
"futures",
|
||||
"hex",
|
||||
"monero-consensus",
|
||||
"monero-epee-bin-serde",
|
||||
"monero-serai",
|
||||
"monero-wire",
|
||||
"multiexp",
|
||||
|
@ -604,6 +604,7 @@ dependencies = [
|
|||
"fixed-bytes",
|
||||
"hex",
|
||||
"paste",
|
||||
"ref-cast",
|
||||
"sealed",
|
||||
"thiserror",
|
||||
]
|
||||
|
@ -1142,16 +1143,6 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monero-epee-bin-serde"
|
||||
version = "1.0.1"
|
||||
source = "git+https://github.com/monero-rs/monero-epee-bin-serde.git?rev=fae7a23#fae7a23f8e57f19553c341c0878b4f0fa5a6994d"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monero-generators"
|
||||
version = "0.4.0"
|
||||
|
@ -1232,11 +1223,11 @@ dependencies = [
|
|||
name = "monero-wire"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"epee-encoding",
|
||||
"fixed-bytes",
|
||||
"hex",
|
||||
"levin-cuprate",
|
||||
"monero-epee-bin-serde",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
@ -1587,6 +1578,26 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f"
|
||||
dependencies = [
|
||||
"ref-cast-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast-impl"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
|
@ -1770,15 +1781,6 @@ dependencies = [
|
|||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.195"
|
||||
|
|
|
@ -21,7 +21,7 @@ binaries = [
|
|||
"dep:tracing-subscriber",
|
||||
"dep:serde_json",
|
||||
"dep:serde",
|
||||
"dep:monero-epee-bin-serde",
|
||||
"dep:epee-encoding",
|
||||
"dep:monero-wire",
|
||||
"dep:borsh",
|
||||
"dep:dirs",
|
||||
|
@ -51,7 +51,7 @@ hex = "0.4"
|
|||
|
||||
# used in binaries
|
||||
monero-wire = {path="../net/monero-wire", optional = true}
|
||||
monero-epee-bin-serde = { workspace = true , optional = true}
|
||||
epee-encoding = { path="../net/epee-encoding" , optional = true}
|
||||
serde_json = {version = "1", optional = true}
|
||||
serde = {version = "1", optional = true, features = ["derive"]}
|
||||
tracing-subscriber = {version = "0.3", optional = true}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::ops::Deref;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
ops::Range,
|
||||
|
@ -15,9 +16,9 @@ use monero_serai::{
|
|||
rpc::{HttpRpc, Rpc},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use monero_wire::common::{BlockCompleteEntry, TransactionBlobs};
|
||||
use monero_wire::common::TransactionBlobs;
|
||||
use rayon::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use tokio::{
|
||||
sync::RwLock,
|
||||
|
@ -174,41 +175,56 @@ impl RpcConnection {
|
|||
) -> Result<Vec<(Block, Vec<Transaction>)>, tower::BoxError> {
|
||||
tracing::info!("Getting blocks in range: {:?}", range);
|
||||
|
||||
#[derive(Serialize)]
|
||||
mod items {
|
||||
use monero_wire::common::BlockCompleteEntry;
|
||||
|
||||
pub struct Request {
|
||||
pub heights: Vec<u64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
epee_encoding::epee_object!(
|
||||
Request,
|
||||
heights: Vec<u64>,
|
||||
);
|
||||
|
||||
pub struct Response {
|
||||
pub blocks: Vec<BlockCompleteEntry>,
|
||||
}
|
||||
|
||||
epee_encoding::epee_object!(
|
||||
Response,
|
||||
blocks: Vec<BlockCompleteEntry>,
|
||||
);
|
||||
}
|
||||
use items::*;
|
||||
|
||||
let res = self
|
||||
.con
|
||||
.bin_call(
|
||||
"get_blocks_by_height.bin",
|
||||
monero_epee_bin_serde::to_bytes(&Request {
|
||||
epee_encoding::to_bytes(Request {
|
||||
heights: range.collect(),
|
||||
})?,
|
||||
})?
|
||||
.to_vec(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let address = self.address.clone();
|
||||
rayon_spawn_async(move || {
|
||||
let blocks: Response = monero_epee_bin_serde::from_bytes(res)?;
|
||||
let blocks: Response =
|
||||
epee_encoding::from_bytes(&mut epee_encoding::macros::bytes::Bytes::from(res))?;
|
||||
|
||||
blocks
|
||||
.blocks
|
||||
.into_par_iter()
|
||||
.map(|b| {
|
||||
let block = Block::read(&mut b.block.as_slice())?;
|
||||
let block = Block::read(&mut b.block.deref())?;
|
||||
|
||||
let txs = match b.txs {
|
||||
TransactionBlobs::Pruned(_) => return Err("node sent pruned txs!".into()),
|
||||
TransactionBlobs::Normal(txs) => txs
|
||||
.into_par_iter()
|
||||
.map(|tx| Transaction::read(&mut tx.as_slice()))
|
||||
.map(|tx| Transaction::read(&mut tx.deref()))
|
||||
.collect::<Result<_, _>>()?,
|
||||
TransactionBlobs::None => vec![],
|
||||
};
|
||||
|
@ -237,30 +253,57 @@ impl RpcConnection {
|
|||
out_ids.values().map(|amt_map| amt_map.len()).sum::<usize>()
|
||||
);
|
||||
|
||||
#[derive(Serialize, Copy, Clone)]
|
||||
struct OutputID {
|
||||
mod items {
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct OutputID {
|
||||
pub amount: u64,
|
||||
pub index: u64,
|
||||
}
|
||||
|
||||
epee_encoding::epee_object!(
|
||||
OutputID,
|
||||
amount: u64,
|
||||
index: u64,
|
||||
);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Request {
|
||||
pub outputs: Vec<OutputID>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
struct Request<'a> {
|
||||
outputs: &'a [OutputID],
|
||||
epee_encoding::epee_object!(
|
||||
Request,
|
||||
outputs: Vec<OutputID>,
|
||||
);
|
||||
|
||||
pub struct OutputRes {
|
||||
pub height: u64,
|
||||
pub key: [u8; 32],
|
||||
pub mask: [u8; 32],
|
||||
pub txid: [u8; 32],
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct OutputRes {
|
||||
epee_encoding::epee_object!(
|
||||
OutputRes,
|
||||
height: u64,
|
||||
key: [u8; 32],
|
||||
mask: [u8; 32],
|
||||
txid: [u8; 32],
|
||||
);
|
||||
|
||||
pub struct Response {
|
||||
pub outs: Vec<OutputRes>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Response {
|
||||
epee_encoding::epee_object!(
|
||||
Response,
|
||||
outs: Vec<OutputRes>,
|
||||
);
|
||||
}
|
||||
|
||||
use items::*;
|
||||
|
||||
let outputs = rayon_spawn_async(|| {
|
||||
out_ids
|
||||
.into_iter()
|
||||
|
@ -281,7 +324,10 @@ impl RpcConnection {
|
|||
.con
|
||||
.bin_call(
|
||||
"get_outs.bin",
|
||||
monero_epee_bin_serde::to_bytes(&Request { outputs: &outputs })?,
|
||||
epee_encoding::to_bytes(Request {
|
||||
outputs: outputs.clone(),
|
||||
})?
|
||||
.to_vec(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -289,7 +335,8 @@ impl RpcConnection {
|
|||
|
||||
let span = tracing::Span::current();
|
||||
rayon_spawn_async(move || {
|
||||
let outs: Response = monero_epee_bin_serde::from_bytes(&res)?;
|
||||
let outs: Response =
|
||||
epee_encoding::from_bytes(&mut epee_encoding::macros::bytes::Bytes::from(res))?;
|
||||
|
||||
tracing::info!(parent: &span, "Got outputs len: {}", outs.outs.len());
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ fixed-bytes = { path = "../fixed-bytes", default-features = false }
|
|||
|
||||
sealed = "0.5.0"
|
||||
paste = "1.0.14"
|
||||
ref-cast = "1.0.22"
|
||||
bytes = { workspace = true }
|
||||
thiserror = { workspace = true, optional = true}
|
||||
|
||||
|
|
99
net/epee-encoding/src/container_as_blob.rs
Normal file
99
net/epee-encoding/src/container_as_blob.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||
use ref_cast::RefCast;
|
||||
use sealed::sealed;
|
||||
|
||||
use crate::{error::*, value::*, EpeeValue, InnerMarker, Marker};
|
||||
|
||||
#[derive(RefCast)]
|
||||
#[repr(transparent)]
|
||||
pub struct ContainerAsBlob<T: Containerable + EpeeValue>(Vec<T>);
|
||||
|
||||
impl<T: Containerable + EpeeValue> From<Vec<T>> for ContainerAsBlob<T> {
|
||||
fn from(value: Vec<T>) -> Self {
|
||||
ContainerAsBlob(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Containerable + EpeeValue> From<ContainerAsBlob<T>> for Vec<T> {
|
||||
fn from(value: ContainerAsBlob<T>) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Containerable + EpeeValue> From<&'a Vec<T>> for &'a ContainerAsBlob<T> {
|
||||
fn from(value: &'a Vec<T>) -> Self {
|
||||
ContainerAsBlob::ref_cast(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[sealed]
|
||||
impl<T: Containerable + EpeeValue> EpeeValue for ContainerAsBlob<T> {
|
||||
const MARKER: Marker = Marker::new(InnerMarker::String);
|
||||
|
||||
fn read<B: Buf>(r: &mut B, marker: &Marker) -> Result<Self> {
|
||||
let bytes = Bytes::read(r, marker)?;
|
||||
if bytes.len() % T::SIZE != 0 {
|
||||
return Err(Error::Value("Can't convert blob container to Vec type."));
|
||||
}
|
||||
|
||||
Ok(ContainerAsBlob(
|
||||
bytes
|
||||
.windows(T::SIZE)
|
||||
.step_by(T::SIZE)
|
||||
.map(T::from_bytes)
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
fn should_write(&self) -> bool {
|
||||
!self.0.is_empty()
|
||||
}
|
||||
|
||||
fn epee_default_value() -> Option<Self> {
|
||||
Some(ContainerAsBlob(vec![]))
|
||||
}
|
||||
|
||||
fn write<B: BufMut>(self, w: &mut B) -> crate::Result<()> {
|
||||
let mut buf = BytesMut::with_capacity(self.0.len() * T::SIZE);
|
||||
self.0.iter().for_each(|tt| tt.push_bytes(&mut buf));
|
||||
buf.write(w)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Containerable {
|
||||
const SIZE: usize;
|
||||
|
||||
/// Returns `Self` from bytes.
|
||||
///
|
||||
/// `bytes` is guaranteed to be [`Self::SIZE`] long.
|
||||
fn from_bytes(bytes: &[u8]) -> Self;
|
||||
|
||||
fn push_bytes(&self, buf: &mut BytesMut);
|
||||
}
|
||||
|
||||
macro_rules! int_container_able {
|
||||
($int:ty ) => {
|
||||
impl Containerable for $int {
|
||||
const SIZE: usize = std::mem::size_of::<$int>();
|
||||
|
||||
fn from_bytes(bytes: &[u8]) -> Self {
|
||||
<$int>::from_le_bytes(bytes.try_into().unwrap())
|
||||
}
|
||||
|
||||
fn push_bytes(&self, buf: &mut BytesMut) {
|
||||
buf.put_slice(&self.to_le_bytes())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int_container_able!(u16);
|
||||
int_container_able!(u32);
|
||||
int_container_able!(u64);
|
||||
int_container_able!(u128);
|
||||
|
||||
int_container_able!(i8);
|
||||
int_container_able!(i16);
|
||||
int_container_able!(i32);
|
||||
int_container_able!(i64);
|
||||
int_container_able!(i128);
|
|
@ -51,7 +51,7 @@
|
|||
//!
|
||||
//!
|
||||
//! let data = [1, 17, 1, 1, 1, 1, 2, 1, 1, 4, 3, 118, 97, 108, 5, 4, 0, 0, 0, 0, 0, 0, 0]; // the data to decode;
|
||||
//! let val: Test = from_bytes(&data).unwrap();
|
||||
//! let val: Test = from_bytes(&mut &data).unwrap();
|
||||
//! let data = to_bytes(val).unwrap();
|
||||
//!
|
||||
//!
|
||||
|
@ -79,18 +79,18 @@
|
|||
//!
|
||||
//!
|
||||
//! let data = [1, 17, 1, 1, 1, 1, 2, 1, 1, 4, 3, 118, 97, 108, 5, 4, 0, 0, 0, 0, 0, 0, 0]; // the data to decode;
|
||||
//! let val: Test2 = from_bytes(&data).unwrap();
|
||||
//! let val: Test2 = from_bytes(&mut &data).unwrap();
|
||||
//! let data = to_bytes(val).unwrap();
|
||||
//!
|
||||
//! ```
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::{ops::Deref, str::from_utf8 as str_from_utf8};
|
||||
|
||||
use bytes::{Buf, BufMut, Bytes};
|
||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||
|
||||
pub mod container_as_blob;
|
||||
pub mod error;
|
||||
mod io;
|
||||
pub mod macros;
|
||||
|
@ -139,13 +139,13 @@ pub trait EpeeObject: Sized {
|
|||
}
|
||||
|
||||
/// Read the object `T` from a byte array.
|
||||
pub fn from_bytes<T: EpeeObject>(mut buf: &[u8]) -> Result<T> {
|
||||
read_head_object(&mut buf)
|
||||
pub fn from_bytes<T: EpeeObject, B: Buf>(buf: &mut B) -> Result<T> {
|
||||
read_head_object(buf)
|
||||
}
|
||||
|
||||
/// Turn the object into epee bytes.
|
||||
pub fn to_bytes<T: EpeeObject>(val: T) -> Result<Vec<u8>> {
|
||||
let mut buf = Vec::<u8>::new();
|
||||
pub fn to_bytes<T: EpeeObject>(val: T) -> Result<BytesMut> {
|
||||
let mut buf = BytesMut::new();
|
||||
write_head_object(val, &mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ pub use paste::paste;
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! field_name {
|
||||
($field: ident, $alt_name: literal) => {
|
||||
($field: tt, $alt_name: tt) => {
|
||||
$alt_name
|
||||
};
|
||||
($field: ident,) => {
|
||||
|
@ -21,11 +21,21 @@ macro_rules! field_ty {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! try_right_then_left {
|
||||
($a:expr, $b:expr) => {
|
||||
$b
|
||||
};
|
||||
($a:expr,) => {
|
||||
$a
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! epee_object {
|
||||
(
|
||||
$obj:ident,
|
||||
$($field: ident $(($alt_name: literal))?: $ty:ty $(= $default:literal)? $(as $ty_as:ty)?, )+
|
||||
$($field: ident $(($alt_name: literal))?: $ty:ty $(as $ty_as:ty )? $(= $default:expr)? $(=> $read_fn:expr, $write_fn:expr, $should_write_fn:expr)?, )+
|
||||
$(!flatten: $($flat_field: ident: $flat_ty:ty ,)+)?
|
||||
|
||||
) => {
|
||||
|
@ -44,7 +54,9 @@ macro_rules! epee_object {
|
|||
fn add_field<B: epee_encoding::macros::bytes::Buf>(&mut self, name: &str, b: &mut B) -> epee_encoding::error::Result<bool> {
|
||||
match name {
|
||||
$(epee_encoding::field_name!($field, $($alt_name)?) => {
|
||||
if core::mem::replace(&mut self.$field, Some(epee_encoding::read_epee_value(b)?)).is_some() {
|
||||
if core::mem::replace(&mut self.$field, Some(
|
||||
epee_encoding::try_right_then_left!(epee_encoding::read_epee_value(b)?, $($read_fn(b)?)?)
|
||||
)).is_some() {
|
||||
Err(epee_encoding::error::Error::Value("Duplicate field in data"))?;
|
||||
}
|
||||
Ok(true)
|
||||
|
@ -65,11 +77,18 @@ macro_rules! epee_object {
|
|||
Ok(
|
||||
$obj {
|
||||
$(
|
||||
$field: self.$field
|
||||
$field: {
|
||||
let epee_default_value = epee_encoding::try_right_then_left!(epee_encoding::EpeeValue::epee_default_value(), $({
|
||||
let _ = $should_write_fn;
|
||||
None
|
||||
})?);
|
||||
|
||||
self.$field
|
||||
$(.or(Some($default)))?
|
||||
.or(epee_encoding::EpeeValue::epee_default_value())
|
||||
.or(epee_default_value)
|
||||
$(.map(<$ty_as>::into))?
|
||||
.ok_or(epee_encoding::error::Error::Value("Missing field in data"))?,
|
||||
.ok_or(epee_encoding::error::Error::Value("Missing field in data"))?
|
||||
},
|
||||
)+
|
||||
|
||||
$(
|
||||
|
@ -90,7 +109,10 @@ macro_rules! epee_object {
|
|||
let mut fields = 0;
|
||||
|
||||
$(
|
||||
if $(&self.$field != &$default &&)? epee_encoding::EpeeValue::should_write($(<&$ty_as>::from)?( &self.$field) ) {
|
||||
let field = epee_encoding::try_right_then_left!(&self.$field, $(<&$ty_as>::from(&self.$field))? );
|
||||
|
||||
if $((field) != &$default &&)? epee_encoding::try_right_then_left!(epee_encoding::EpeeValue::should_write, $($should_write_fn)?)(field )
|
||||
{
|
||||
fields += 1;
|
||||
}
|
||||
)+
|
||||
|
@ -106,8 +128,11 @@ macro_rules! epee_object {
|
|||
|
||||
fn write_fields<B: epee_encoding::macros::bytes::BufMut>(self, w: &mut B) -> epee_encoding::error::Result<()> {
|
||||
$(
|
||||
if $(&self.$field != &$default &&)? epee_encoding::EpeeValue::should_write($(<&$ty_as>::from)?( &self.$field) ) {
|
||||
epee_encoding::write_field($(<$ty_as>::from)?(self.$field), epee_encoding::field_name!($field, $($alt_name)?), w)?;
|
||||
let field = epee_encoding::try_right_then_left!(self.$field, $(<$ty_as>::from(self.$field))? );
|
||||
|
||||
if $(field != $default &&)? epee_encoding::try_right_then_left!(epee_encoding::EpeeValue::should_write, $($should_write_fn)?)(&field )
|
||||
{
|
||||
epee_encoding::try_right_then_left!(epee_encoding::write_field, $($write_fn)?)((field), epee_encoding::field_name!($field, $($alt_name)?), w)?;
|
||||
}
|
||||
)+
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use alloc::{string::String, vec::Vec};
|
|||
/// the different possible base epee values.
|
||||
use core::fmt::Debug;
|
||||
|
||||
use bytes::{Buf, BufMut, Bytes};
|
||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||
use sealed::sealed;
|
||||
|
||||
use fixed_bytes::{ByteArray, ByteArrayVec};
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
|||
/// A trait for epee values, this trait is sealed as all possible epee values are
|
||||
/// defined in the lib, to make an [`EpeeValue`] outside the lib you will need to
|
||||
/// use the trait [`EpeeObject`].
|
||||
#[sealed]
|
||||
#[sealed(pub(crate))]
|
||||
pub trait EpeeValue: Sized {
|
||||
const MARKER: Marker;
|
||||
|
||||
|
@ -234,6 +234,42 @@ impl EpeeValue for Bytes {
|
|||
}
|
||||
}
|
||||
|
||||
#[sealed::sealed]
|
||||
impl EpeeValue for BytesMut {
|
||||
const MARKER: Marker = Marker::new(InnerMarker::String);
|
||||
|
||||
fn read<B: Buf>(r: &mut B, marker: &Marker) -> Result<Self> {
|
||||
if marker != &Self::MARKER {
|
||||
return Err(Error::Format("Marker does not match expected Marker"));
|
||||
}
|
||||
|
||||
let len = read_varint(r)?;
|
||||
if len > MAX_STRING_LEN_POSSIBLE {
|
||||
return Err(Error::Format("Byte array exceeded max length"));
|
||||
}
|
||||
|
||||
if r.remaining() < len.try_into()? {
|
||||
return Err(Error::IO("Not enough bytes to fill object"));
|
||||
}
|
||||
|
||||
let mut bytes = BytesMut::zeroed(len.try_into()?);
|
||||
r.copy_to_slice(&mut bytes);
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
|
||||
write_varint(self.len().try_into()?, w)?;
|
||||
|
||||
if w.remaining_mut() < self.len() {
|
||||
return Err(Error::IO("Not enough capacity to write bytes"));
|
||||
}
|
||||
|
||||
w.put(self);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[sealed::sealed]
|
||||
impl<const N: usize> EpeeValue for ByteArrayVec<N> {
|
||||
const MARKER: Marker = Marker::new(InnerMarker::String);
|
||||
|
@ -481,6 +517,8 @@ epee_seq!(f64);
|
|||
epee_seq!(bool);
|
||||
epee_seq!(Vec<u8>);
|
||||
epee_seq!(String);
|
||||
epee_seq!(Bytes);
|
||||
epee_seq!(BytesMut);
|
||||
|
||||
#[sealed]
|
||||
impl<T: EpeeValue> EpeeValue for Option<T> {
|
||||
|
|
|
@ -27,7 +27,7 @@ fn epee_alt_name() {
|
|||
let val2 = AltName2 { val2: 40, d: 30 };
|
||||
let bytes = to_bytes(val2).unwrap();
|
||||
|
||||
let val: AltName = from_bytes(&bytes).unwrap();
|
||||
let val: AltName = from_bytes(&mut bytes.clone()).unwrap();
|
||||
|
||||
let bytes2 = to_bytes(val).unwrap();
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ fn duplicate_key() {
|
|||
b'a', 0x0B, 0x00,
|
||||
];
|
||||
|
||||
assert!(from_bytes::<T>(&data).is_err());
|
||||
assert!(from_bytes::<T, _>(&mut &data[..]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -35,5 +35,5 @@ fn duplicate_key_with_default() {
|
|||
b'a', 0x0B, 0x00,
|
||||
];
|
||||
|
||||
assert!(from_bytes::<TT>(&data).is_err());
|
||||
assert!(from_bytes::<TT, _>(&mut &data[..]).is_err());
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ pub struct Optional {
|
|||
epee_object!(
|
||||
Optional,
|
||||
val: u8,
|
||||
optional_val: i32 = -4,
|
||||
optional_val: i32 = -4_i32,
|
||||
);
|
||||
pub struct NotOptional {
|
||||
val: u8,
|
||||
|
@ -37,11 +37,11 @@ fn epee_default_does_not_encode() {
|
|||
val: 1,
|
||||
optional_val: -4,
|
||||
};
|
||||
let bytes = to_bytes(val).unwrap();
|
||||
let mut bytes = to_bytes(val).unwrap().freeze();
|
||||
|
||||
assert!(from_bytes::<NotOptional>(&bytes).is_err());
|
||||
assert!(from_bytes::<NotOptional, _>(&mut bytes.clone()).is_err());
|
||||
|
||||
let val: Optional = from_bytes(&bytes).unwrap();
|
||||
let val: Optional = from_bytes(&mut bytes).unwrap();
|
||||
assert_eq!(val.optional_val, -4);
|
||||
assert_eq!(val.val, 1);
|
||||
}
|
||||
|
@ -52,11 +52,11 @@ fn epee_non_default_does_encode() {
|
|||
val: 8,
|
||||
optional_val: -3,
|
||||
};
|
||||
let bytes = to_bytes(val).unwrap();
|
||||
let mut bytes = to_bytes(val).unwrap().freeze();
|
||||
|
||||
assert!(from_bytes::<NotOptional>(&bytes).is_ok());
|
||||
assert!(from_bytes::<NotOptional, _>(&mut bytes.clone()).is_ok());
|
||||
|
||||
let val: Optional = from_bytes(&bytes).unwrap();
|
||||
let val: Optional = from_bytes(&mut bytes).unwrap();
|
||||
assert_eq!(val.optional_val, -3);
|
||||
assert_eq!(val.val, 8)
|
||||
}
|
||||
|
@ -64,11 +64,11 @@ fn epee_non_default_does_encode() {
|
|||
#[test]
|
||||
fn epee_value_not_present_with_default() {
|
||||
let val = NotPresent { val: 76 };
|
||||
let bytes = to_bytes(val).unwrap();
|
||||
let mut bytes = to_bytes(val).unwrap().freeze();
|
||||
|
||||
assert!(from_bytes::<NotOptional>(&bytes).is_err());
|
||||
assert!(from_bytes::<NotOptional, _>(&mut bytes.clone()).is_err());
|
||||
|
||||
let val: Optional = from_bytes(&bytes).unwrap();
|
||||
let val: Optional = from_bytes(&mut bytes).unwrap();
|
||||
assert_eq!(val.optional_val, -4);
|
||||
assert_eq!(val.val, 76)
|
||||
}
|
||||
|
|
|
@ -43,9 +43,9 @@ fn epee_flatten() {
|
|||
val: 94,
|
||||
val2: vec![4, 5],
|
||||
};
|
||||
let bytes = to_bytes(val2.clone()).unwrap();
|
||||
let mut bytes = to_bytes(val2.clone()).unwrap();
|
||||
|
||||
let val: Parent = from_bytes(&bytes).unwrap();
|
||||
let val: Parent = from_bytes(&mut bytes).unwrap();
|
||||
|
||||
assert_eq!(val.child.val2, val2.val2);
|
||||
assert_eq!(val.child.val, val2.val);
|
||||
|
@ -95,8 +95,8 @@ epee_object!(
|
|||
fn epee_double_flatten() {
|
||||
let val = Parent12::default();
|
||||
|
||||
let bytes = to_bytes(val.clone()).unwrap();
|
||||
let val1: Parent12 = from_bytes(&bytes).unwrap();
|
||||
let mut bytes = to_bytes(val.clone()).unwrap();
|
||||
let val1: Parent12 = from_bytes(&mut bytes).unwrap();
|
||||
|
||||
assert_eq!(val, val1);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use epee_encoding::{epee_object, from_bytes, to_bytes};
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct T {
|
||||
|
@ -11,9 +12,10 @@ epee_object!(
|
|||
);
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::useless_asref)]
|
||||
fn optional_val_not_in_data() {
|
||||
let bytes: &[u8] = b"\x01\x11\x01\x01\x01\x01\x02\x01\x01\x00";
|
||||
let t: T = from_bytes(bytes).unwrap();
|
||||
let t: T = from_bytes(&mut bytes.as_ref()).unwrap();
|
||||
let bytes2 = to_bytes(t.clone()).unwrap();
|
||||
assert_eq!(bytes, bytes2);
|
||||
assert!(t.val.is_none());
|
||||
|
@ -24,8 +26,8 @@ fn optional_val_in_data() {
|
|||
let bytes = [
|
||||
0x01, 0x11, 0x01, 0x1, 0x01, 0x01, 0x02, 0x1, 0x1, 0x04, 0x03, b'v', b'a', b'l', 0x08, 21,
|
||||
];
|
||||
let t: T = from_bytes(&bytes).unwrap();
|
||||
let t: T = from_bytes(&mut &bytes[..]).unwrap();
|
||||
let bytes2 = to_bytes(t.clone()).unwrap();
|
||||
assert_eq!(bytes.as_slice(), bytes2.as_slice());
|
||||
assert_eq!(bytes.as_slice(), bytes2.deref());
|
||||
assert_eq!(t.val.unwrap(), 21);
|
||||
}
|
||||
|
|
|
@ -51,9 +51,9 @@ epee_object!(
|
|||
fn p2p_handshake() {
|
||||
let bytes = hex::decode("01110101010102010108096e6f64655f646174610c10076d795f706f727406a04600000a6e6574776f726b5f69640a401230f171610441611731008216a1a11007706565725f6964053eb3c096c4471c340d737570706f72745f666c61677306010000000c7061796c6f61645f646174610c181563756d756c61746976655f646966666963756c7479053951f7a79aab4a031b63756d756c61746976655f646966666963756c74795f746f7036340500000000000000000e63757272656e745f68656967687405fa092a00000000000c7072756e696e675f73656564068001000006746f705f69640a806cc497b230ba57a95edb370be8d6870c94e0992937c89b1def3a4cb7726d37ad0b746f705f76657273696f6e0810").unwrap();
|
||||
|
||||
let val: HandshakeR = from_bytes(&bytes).unwrap();
|
||||
let val: HandshakeR = from_bytes(&mut bytes.as_slice()).unwrap();
|
||||
|
||||
let bytes = to_bytes(val.clone()).unwrap();
|
||||
let mut bytes = to_bytes(val.clone()).unwrap();
|
||||
|
||||
assert_eq!(val, from_bytes(&bytes).unwrap());
|
||||
assert_eq!(val, from_bytes(&mut bytes).unwrap());
|
||||
}
|
||||
|
|
|
@ -63,21 +63,21 @@ epee_object!(
|
|||
#[test]
|
||||
fn rpc_get_outs_response() {
|
||||
let bytes = hex::decode("011101010101020101140763726564697473050000000000000000046f7574738c04140668656967687405a100000000000000036b65790a802d392d0be38eb4699c17767e62a063b8d2f989ec15c80e5d2665ab06f8397439046d61736b0a805e8b863c5b267deda13f4bc5d5ec8e59043028380f2431bc8691c15c83e1fea404747869640a80c0646e065a33b849f0d9563673ca48eb0c603fe721dd982720dba463172c246f08756e6c6f636b65640b00067374617475730a084f4b08746f705f686173680a0009756e747275737465640b00").unwrap();
|
||||
let val: GetOutsResponse = from_bytes(&bytes).unwrap();
|
||||
let bytes = to_bytes(val.clone()).unwrap();
|
||||
let val: GetOutsResponse = from_bytes(&mut bytes.as_slice()).unwrap();
|
||||
let mut bytes = to_bytes(val.clone()).unwrap();
|
||||
|
||||
assert_eq!(val, from_bytes(&bytes).unwrap());
|
||||
assert_eq!(val, from_bytes(&mut bytes).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_out_indexes_response() {
|
||||
let bytes = [
|
||||
let bytes: [u8; 61] = [
|
||||
1, 17, 1, 1, 1, 1, 2, 1, 1, 16, 7, 99, 114, 101, 100, 105, 116, 115, 5, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 6, 115, 116, 97, 116, 117, 115, 10, 8, 79, 75, 8, 116, 111, 112, 95, 104, 97, 115,
|
||||
104, 10, 0, 9, 117, 110, 116, 114, 117, 115, 116, 101, 100, 11, 0,
|
||||
];
|
||||
let val: GetOIndexesResponse = from_bytes(&bytes).unwrap();
|
||||
let bytes = to_bytes(val.clone()).unwrap();
|
||||
let val: GetOIndexesResponse = from_bytes(&mut bytes.as_slice()).unwrap();
|
||||
let mut bytes = to_bytes(val.clone()).unwrap();
|
||||
|
||||
assert_eq!(val, from_bytes(&bytes).unwrap());
|
||||
assert_eq!(val, from_bytes(&mut bytes).unwrap());
|
||||
}
|
||||
|
|
|
@ -28,9 +28,9 @@ fn seq_with_zero_len_can_have_any_marker() {
|
|||
data.push(0x80 | marker);
|
||||
data.push(0);
|
||||
|
||||
assert!(from_bytes::<ObjSeq>(&data).is_ok());
|
||||
assert!(from_bytes::<ObjSeq, _>(&mut data.as_slice()).is_ok());
|
||||
|
||||
assert!(from_bytes::<ValSeq>(&data).is_ok());
|
||||
assert!(from_bytes::<ValSeq, _>(&mut data.as_slice()).is_ok());
|
||||
|
||||
data.drain(14..);
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ fn seq_with_non_zero_len_must_have_correct_marker() {
|
|||
data.push(0x04); // varint length of 1
|
||||
data.extend_from_slice(&1_i64.to_le_bytes());
|
||||
|
||||
assert!(from_bytes::<ValSeq>(&data).is_err());
|
||||
assert!(from_bytes::<ValSeq, _>(&mut data.as_slice()).is_err());
|
||||
|
||||
data.drain(14..);
|
||||
}
|
||||
|
@ -56,5 +56,5 @@ fn seq_with_non_zero_len_must_have_correct_marker() {
|
|||
data.push(0x80 + 1);
|
||||
data.push(0x04); // varint length
|
||||
data.extend_from_slice(&1_i64.to_le_bytes());
|
||||
(from_bytes::<ValSeq>(&data).unwrap());
|
||||
(from_bytes::<ValSeq, _>(&mut data.as_slice()).unwrap());
|
||||
}
|
||||
|
|
|
@ -735,7 +735,7 @@ fn stack_overlfow() {
|
|||
8, 7, 1, 100, 12, 8, 3, 118, 97, 108, 8, 7,
|
||||
];
|
||||
|
||||
let obj: Result<Q, _> = from_bytes(&bytes);
|
||||
let obj: Result<Q, _> = from_bytes(&mut bytes.as_slice());
|
||||
|
||||
assert!(obj.is_err())
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ impl Debug for FixedByteError {
|
|||
///
|
||||
/// Internally this is just a wrapper around [`Bytes`], with the constructors checking that the length is equal to [`N`].
|
||||
/// This implements [`Deref`] with the target being `[u8; N]`.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ByteArray<const N: usize>(Bytes);
|
||||
|
||||
impl<const N: usize> ByteArray<N> {
|
||||
|
@ -49,6 +50,12 @@ impl<const N: usize> ByteArray<N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<[u8; N]> for ByteArray<N> {
|
||||
fn from(value: [u8; N]) -> Self {
|
||||
ByteArray(Bytes::copy_from_slice(&value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Deref for ByteArray<N> {
|
||||
type Target = [u8; N];
|
||||
|
||||
|
@ -68,6 +75,18 @@ impl<const N: usize> TryFrom<Bytes> for ByteArray<N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> TryFrom<Vec<u8>> for ByteArray<N> {
|
||||
type Error = FixedByteError;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if value.len() != N {
|
||||
return Err(FixedByteError::InvalidLength);
|
||||
}
|
||||
Ok(ByteArray(Bytes::from(value)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ByteArrayVec<const N: usize>(Bytes);
|
||||
|
||||
impl<const N: usize> ByteArrayVec<N> {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||
use tokio_util::codec::{Decoder, Encoder};
|
||||
|
||||
use crate::{
|
||||
|
@ -111,7 +111,7 @@ impl<C: LevinCommand> Decoder for LevinBucketCodec<C> {
|
|||
|
||||
return Ok(Some(Bucket {
|
||||
header,
|
||||
body: src.copy_to_bytes(body_len).into(),
|
||||
body: src.copy_to_bytes(body_len),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ impl<C: LevinCommand> Encoder<Bucket<C>> for LevinBucketCodec<C> {
|
|||
enum MessageState<C> {
|
||||
#[default]
|
||||
WaitingForBucket,
|
||||
WaitingForRestOfFragment(Vec<u8>, MessageType, C),
|
||||
WaitingForRestOfFragment(Vec<Bytes>, MessageType, C),
|
||||
}
|
||||
|
||||
/// A tokio-codec for levin messages or in other words the decoded body
|
||||
|
@ -167,7 +167,7 @@ impl<T: LevinBody> Decoder for LevinMessageCodec<T> {
|
|||
loop {
|
||||
match &mut self.state {
|
||||
MessageState::WaitingForBucket => {
|
||||
let Some(bucket) = self.bucket_codec.decode(src)? else {
|
||||
let Some(mut bucket) = self.bucket_codec.decode(src)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
|
@ -199,7 +199,7 @@ impl<T: LevinBody> Decoder for LevinMessageCodec<T> {
|
|||
let _ = std::mem::replace(
|
||||
&mut self.state,
|
||||
MessageState::WaitingForRestOfFragment(
|
||||
bucket.body.to_vec(),
|
||||
vec![bucket.body],
|
||||
message_type,
|
||||
bucket.header.command,
|
||||
),
|
||||
|
@ -209,7 +209,7 @@ impl<T: LevinBody> Decoder for LevinMessageCodec<T> {
|
|||
}
|
||||
|
||||
return Ok(Some(T::decode_message(
|
||||
&bucket.body,
|
||||
&mut bucket.body,
|
||||
message_type,
|
||||
bucket.header.command,
|
||||
)?));
|
||||
|
@ -257,16 +257,23 @@ impl<T: LevinBody> Decoder for LevinMessageCodec<T> {
|
|||
));
|
||||
}
|
||||
|
||||
bytes.append(&mut bucket.body.to_vec());
|
||||
bytes.push(bucket.body);
|
||||
|
||||
if flags.is_end_fragment() {
|
||||
let MessageState::WaitingForRestOfFragment(bytes, ty, command) =
|
||||
let MessageState::WaitingForRestOfFragment(mut bytes, ty, command) =
|
||||
std::mem::replace(&mut self.state, MessageState::WaitingForBucket)
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
return Ok(Some(T::decode_message(&bytes, ty, command)?));
|
||||
// TODO: this doesn't seem very efficient but I can't think of a better way.
|
||||
bytes.reverse();
|
||||
let mut byte_vec: Box<dyn Buf> = Box::new(bytes.pop().unwrap());
|
||||
for bytes in bytes {
|
||||
byte_vec = Box::new(byte_vec.chain(bytes));
|
||||
}
|
||||
|
||||
return Ok(Some(T::decode_message(&mut byte_vec, ty, command)?));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ pub use header::BucketHead;
|
|||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use bytes::{Buf, Bytes};
|
||||
use thiserror::Error;
|
||||
|
||||
const MONERO_PROTOCOL_VERSION: u32 = 1;
|
||||
|
@ -102,7 +103,7 @@ pub struct Bucket<C> {
|
|||
/// The bucket header
|
||||
pub header: BucketHead<C>,
|
||||
/// The bucket body
|
||||
pub body: Vec<u8>,
|
||||
pub body: Bytes,
|
||||
}
|
||||
|
||||
/// An enum representing if the message is a request, response or notification.
|
||||
|
@ -158,7 +159,7 @@ pub struct BucketBuilder<C> {
|
|||
command: Option<C>,
|
||||
return_code: Option<i32>,
|
||||
protocol_version: Option<u32>,
|
||||
body: Option<Vec<u8>>,
|
||||
body: Option<Bytes>,
|
||||
}
|
||||
|
||||
impl<C> Default for BucketBuilder<C> {
|
||||
|
@ -195,7 +196,7 @@ impl<C: LevinCommand> BucketBuilder<C> {
|
|||
self.protocol_version = Some(version)
|
||||
}
|
||||
|
||||
pub fn set_body(&mut self, body: Vec<u8>) {
|
||||
pub fn set_body(&mut self, body: Bytes) {
|
||||
self.body = Some(body)
|
||||
}
|
||||
|
||||
|
@ -222,14 +223,14 @@ pub trait LevinBody: Sized {
|
|||
type Command: LevinCommand;
|
||||
|
||||
/// Decodes the message from the data in the header
|
||||
fn decode_message(
|
||||
body: &[u8],
|
||||
fn decode_message<B: Buf>(
|
||||
body: &mut B,
|
||||
typ: MessageType,
|
||||
command: Self::Command,
|
||||
) -> Result<Self, BucketError>;
|
||||
|
||||
/// Encodes the message
|
||||
fn encode(&self, builder: &mut BucketBuilder<Self::Command>) -> Result<(), BucketError>;
|
||||
fn encode(self, builder: &mut BucketBuilder<Self::Command>) -> Result<(), BucketError>;
|
||||
}
|
||||
|
||||
/// The levin commands.
|
||||
|
|
|
@ -9,9 +9,10 @@ repository = "https://github.com/SyntheticBird45/cuprate/tree/main/net/monero-wi
|
|||
|
||||
[dependencies]
|
||||
levin-cuprate = {path="../levin"}
|
||||
monero-epee-bin-serde = { workspace = true, features = ["container_as_blob"] }
|
||||
serde = { workspace = true, features = ["derive", "std"]}
|
||||
serde_bytes = { workspace = true, features = ["std"]}
|
||||
epee-encoding = { path = "../epee-encoding" }
|
||||
fixed-bytes = { path = "../fixed-bytes" }
|
||||
|
||||
bytes = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
pub mod network_address;
|
||||
pub mod p2p;
|
||||
mod serde_helpers;
|
||||
|
||||
pub use levin_cuprate::BucketError;
|
||||
pub use network_address::{NetZone, NetworkAddress};
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
//! Monero network. Core Monero has 4 main addresses: IPv4, IPv6, Tor,
|
||||
//! I2p. Currently this module only has IPv(4/6).
|
||||
//!
|
||||
use bytes::BufMut;
|
||||
use epee_encoding::{EpeeObject, EpeeValue};
|
||||
use std::{hash::Hash, net, net::SocketAddr};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod serde_helper;
|
||||
use serde_helper::*;
|
||||
|
||||
|
@ -33,13 +33,23 @@ pub enum NetZone {
|
|||
|
||||
/// A network address which can be encoded into the format required
|
||||
/// to send to other Monero peers.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(try_from = "TaggedNetworkAddress")]
|
||||
#[serde(into = "TaggedNetworkAddress")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NetworkAddress {
|
||||
Clear(SocketAddr),
|
||||
}
|
||||
|
||||
impl EpeeObject for NetworkAddress {
|
||||
type Builder = TaggedNetworkAddress;
|
||||
|
||||
fn number_of_fields(&self) -> u64 {
|
||||
2
|
||||
}
|
||||
|
||||
fn write_fields<B: BufMut>(self, w: &mut B) -> epee_encoding::Result<()> {
|
||||
TaggedNetworkAddress::from(self).write(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkAddress {
|
||||
pub fn get_zone(&self) -> NetZone {
|
||||
match self {
|
||||
|
|
|
@ -1,20 +1,53 @@
|
|||
use bytes::Buf;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use epee_encoding::{epee_object, EpeeObjectBuilder};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::NetworkAddress;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub(crate) struct TaggedNetworkAddress {
|
||||
#[serde(rename = "type")]
|
||||
ty: u8,
|
||||
addr: AllFieldsNetworkAddress,
|
||||
#[derive(Default)]
|
||||
pub struct TaggedNetworkAddress {
|
||||
ty: Option<u8>,
|
||||
addr: Option<AllFieldsNetworkAddress>,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
TaggedNetworkAddress,
|
||||
ty: Option<u8>,
|
||||
addr: Option<AllFieldsNetworkAddress>,
|
||||
);
|
||||
|
||||
impl EpeeObjectBuilder<NetworkAddress> for TaggedNetworkAddress {
|
||||
fn add_field<B: Buf>(&mut self, name: &str, b: &mut B) -> epee_encoding::Result<bool> {
|
||||
match name {
|
||||
"type" => {
|
||||
if std::mem::replace(&mut self.ty, Some(epee_encoding::read_epee_value(b)?))
|
||||
.is_some()
|
||||
{
|
||||
return Err(epee_encoding::Error::Format("Duplicate field in data."));
|
||||
}
|
||||
}
|
||||
"addr" => {
|
||||
if std::mem::replace(&mut self.addr, epee_encoding::read_epee_value(b)?).is_some() {
|
||||
return Err(epee_encoding::Error::Format("Duplicate field in data."));
|
||||
}
|
||||
}
|
||||
_ => return Ok(false),
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn finish(self) -> epee_encoding::Result<NetworkAddress> {
|
||||
self.try_into()
|
||||
.map_err(|_| epee_encoding::Error::Value("Invalid network address"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("Invalid network address")]
|
||||
pub(crate) struct InvalidNetworkAddress;
|
||||
pub struct InvalidNetworkAddress;
|
||||
|
||||
impl TryFrom<TaggedNetworkAddress> for NetworkAddress {
|
||||
type Error = InvalidNetworkAddress;
|
||||
|
@ -22,7 +55,8 @@ impl TryFrom<TaggedNetworkAddress> for NetworkAddress {
|
|||
fn try_from(value: TaggedNetworkAddress) -> Result<Self, Self::Error> {
|
||||
value
|
||||
.addr
|
||||
.try_into_network_address(value.ty)
|
||||
.ok_or(InvalidNetworkAddress)?
|
||||
.try_into_network_address(value.ty.ok_or(InvalidNetworkAddress)?)
|
||||
.ok_or(InvalidNetworkAddress)
|
||||
}
|
||||
}
|
||||
|
@ -32,36 +66,40 @@ impl From<NetworkAddress> for TaggedNetworkAddress {
|
|||
match value {
|
||||
NetworkAddress::Clear(addr) => match addr {
|
||||
SocketAddr::V4(addr) => TaggedNetworkAddress {
|
||||
ty: 1,
|
||||
addr: AllFieldsNetworkAddress {
|
||||
ty: Some(1),
|
||||
addr: Some(AllFieldsNetworkAddress {
|
||||
m_ip: Some(u32::from_be_bytes(addr.ip().octets())),
|
||||
m_port: Some(addr.port()),
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
},
|
||||
SocketAddr::V6(addr) => TaggedNetworkAddress {
|
||||
ty: 2,
|
||||
addr: AllFieldsNetworkAddress {
|
||||
ty: Some(2),
|
||||
addr: Some(AllFieldsNetworkAddress {
|
||||
addr: Some(addr.ip().octets()),
|
||||
m_port: Some(addr.port()),
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
#[derive(Default)]
|
||||
struct AllFieldsNetworkAddress {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
m_ip: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
m_port: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
addr: Option<[u8; 16]>,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
AllFieldsNetworkAddress,
|
||||
m_ip: Option<u32>,
|
||||
m_port: Option<u16>,
|
||||
addr: Option<[u8; 16]>,
|
||||
);
|
||||
|
||||
impl AllFieldsNetworkAddress {
|
||||
fn try_into_network_address(self, ty: u8) -> Option<NetworkAddress> {
|
||||
Some(match ty {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
//! This module defines a Monero `Message` enum which contains
|
||||
//! every possible Monero network message (levin body)
|
||||
|
||||
use bytes::{Buf, Bytes, BytesMut};
|
||||
use levin_cuprate::{
|
||||
BucketBuilder, BucketError, LevinBody, LevinCommand as LevinCommandTrait, MessageType,
|
||||
};
|
||||
|
@ -150,23 +151,23 @@ impl From<LevinCommand> for u32 {
|
|||
}
|
||||
}
|
||||
|
||||
fn decode_message<T: serde::de::DeserializeOwned, Ret>(
|
||||
fn decode_message<B: Buf, T: epee_encoding::EpeeObject, Ret>(
|
||||
ret: impl FnOnce(T) -> Ret,
|
||||
buf: &[u8],
|
||||
buf: &mut B,
|
||||
) -> Result<Ret, BucketError> {
|
||||
let t = monero_epee_bin_serde::from_bytes(buf)
|
||||
.map_err(|e| BucketError::BodyDecodingError(e.into()))?;
|
||||
let t = epee_encoding::from_bytes(buf).map_err(|e| BucketError::BodyDecodingError(e.into()))?;
|
||||
Ok(ret(t))
|
||||
}
|
||||
|
||||
fn build_message<T: serde::Serialize>(
|
||||
fn build_message<T: epee_encoding::EpeeObject>(
|
||||
id: LevinCommand,
|
||||
val: &T,
|
||||
val: T,
|
||||
builder: &mut BucketBuilder<LevinCommand>,
|
||||
) -> Result<(), BucketError> {
|
||||
builder.set_command(id);
|
||||
builder.set_body(
|
||||
monero_epee_bin_serde::to_bytes(val)
|
||||
epee_encoding::to_bytes(val)
|
||||
.map(BytesMut::freeze)
|
||||
.map_err(|e| BucketError::BodyDecodingError(e.into()))?,
|
||||
);
|
||||
Ok(())
|
||||
|
@ -201,7 +202,7 @@ impl ProtocolMessage {
|
|||
}
|
||||
}
|
||||
|
||||
fn decode(buf: &[u8], command: LevinCommand) -> Result<Self, BucketError> {
|
||||
fn decode<B: Buf>(buf: &mut B, command: LevinCommand) -> Result<Self, BucketError> {
|
||||
use LevinCommand as C;
|
||||
|
||||
Ok(match command {
|
||||
|
@ -220,7 +221,7 @@ impl ProtocolMessage {
|
|||
})
|
||||
}
|
||||
|
||||
fn build(&self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
|
||||
fn build(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
|
||||
use LevinCommand as C;
|
||||
|
||||
match self {
|
||||
|
@ -236,7 +237,7 @@ impl ProtocolMessage {
|
|||
}
|
||||
ProtocolMessage::ChainRequest(val) => build_message(C::ChainRequest, val, builder)?,
|
||||
ProtocolMessage::ChainEntryResponse(val) => {
|
||||
build_message(C::ChainResponse, &val, builder)?
|
||||
build_message(C::ChainResponse, val, builder)?
|
||||
}
|
||||
ProtocolMessage::NewFluffyBlock(val) => build_message(C::NewFluffyBlock, val, builder)?,
|
||||
ProtocolMessage::FluffyMissingTransactionsRequest(val) => {
|
||||
|
@ -269,7 +270,7 @@ impl RequestMessage {
|
|||
}
|
||||
}
|
||||
|
||||
fn decode(buf: &[u8], command: LevinCommand) -> Result<Self, BucketError> {
|
||||
fn decode<B: Buf>(buf: &mut B, command: LevinCommand) -> Result<Self, BucketError> {
|
||||
use LevinCommand as C;
|
||||
|
||||
Ok(match command {
|
||||
|
@ -281,7 +282,7 @@ impl RequestMessage {
|
|||
})
|
||||
}
|
||||
|
||||
fn build(&self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
|
||||
fn build(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
|
||||
use LevinCommand as C;
|
||||
|
||||
match self {
|
||||
|
@ -289,11 +290,11 @@ impl RequestMessage {
|
|||
RequestMessage::TimedSync(val) => build_message(C::TimedSync, val, builder)?,
|
||||
RequestMessage::Ping => {
|
||||
builder.set_command(C::Ping);
|
||||
builder.set_body(Vec::new());
|
||||
builder.set_body(Bytes::new());
|
||||
}
|
||||
RequestMessage::SupportFlags => {
|
||||
builder.set_command(C::SupportFlags);
|
||||
builder.set_body(Vec::new());
|
||||
builder.set_body(Bytes::new());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -319,7 +320,7 @@ impl ResponseMessage {
|
|||
}
|
||||
}
|
||||
|
||||
fn decode(buf: &[u8], command: LevinCommand) -> Result<Self, BucketError> {
|
||||
fn decode<B: Buf>(buf: &mut B, command: LevinCommand) -> Result<Self, BucketError> {
|
||||
use LevinCommand as C;
|
||||
|
||||
Ok(match command {
|
||||
|
@ -331,7 +332,7 @@ impl ResponseMessage {
|
|||
})
|
||||
}
|
||||
|
||||
fn build(&self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
|
||||
fn build(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
|
||||
use LevinCommand as C;
|
||||
|
||||
match self {
|
||||
|
@ -375,8 +376,8 @@ impl Message {
|
|||
impl LevinBody for Message {
|
||||
type Command = LevinCommand;
|
||||
|
||||
fn decode_message(
|
||||
body: &[u8],
|
||||
fn decode_message<B: Buf>(
|
||||
body: &mut B,
|
||||
typ: MessageType,
|
||||
command: LevinCommand,
|
||||
) -> Result<Self, BucketError> {
|
||||
|
@ -387,7 +388,7 @@ impl LevinBody for Message {
|
|||
})
|
||||
}
|
||||
|
||||
fn encode(&self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
|
||||
fn encode(self, builder: &mut BucketBuilder<LevinCommand>) -> Result<(), BucketError> {
|
||||
match self {
|
||||
Message::Protocol(pro) => {
|
||||
builder.set_message_type(MessageType::Notification);
|
||||
|
|
|
@ -18,12 +18,13 @@
|
|||
//! Admin message requests must be responded to in order unlike
|
||||
//! protocol messages.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use bytes::Bytes;
|
||||
use epee_encoding::epee_object;
|
||||
|
||||
use super::common::{BasicNodeData, CoreSyncData, PeerListEntryBase, PeerSupportFlags};
|
||||
|
||||
/// A Handshake Request
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct HandshakeRequest {
|
||||
/// Basic Node Data
|
||||
pub node_data: BasicNodeData,
|
||||
|
@ -31,54 +32,87 @@ pub struct HandshakeRequest {
|
|||
pub payload_data: CoreSyncData,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
HandshakeRequest,
|
||||
node_data: BasicNodeData,
|
||||
payload_data: CoreSyncData,
|
||||
);
|
||||
|
||||
/// A Handshake Response
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct HandshakeResponse {
|
||||
/// Basic Node Data
|
||||
pub node_data: BasicNodeData,
|
||||
/// Core Sync Data
|
||||
pub payload_data: CoreSyncData,
|
||||
/// PeerList
|
||||
#[serde(default = "Vec::new")]
|
||||
pub local_peerlist_new: Vec<PeerListEntryBase>,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
HandshakeResponse,
|
||||
node_data: BasicNodeData,
|
||||
payload_data: CoreSyncData,
|
||||
local_peerlist_new: Vec<PeerListEntryBase>,
|
||||
);
|
||||
|
||||
/// A TimedSync Request
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TimedSyncRequest {
|
||||
/// Core Sync Data
|
||||
pub payload_data: CoreSyncData,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
TimedSyncRequest,
|
||||
payload_data: CoreSyncData,
|
||||
);
|
||||
|
||||
/// A TimedSync Response
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TimedSyncResponse {
|
||||
/// Core Sync Data
|
||||
pub payload_data: CoreSyncData,
|
||||
/// PeerList
|
||||
#[serde(default = "Vec::new")]
|
||||
pub local_peerlist_new: Vec<PeerListEntryBase>,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
TimedSyncResponse,
|
||||
payload_data: CoreSyncData,
|
||||
local_peerlist_new: Vec<PeerListEntryBase>,
|
||||
);
|
||||
|
||||
/// The status field of an okay ping response
|
||||
pub const PING_OK_RESPONSE_STATUS_TEXT: &str = "OK";
|
||||
pub const PING_OK_RESPONSE_STATUS_TEXT: Bytes = Bytes::from_static("OK".as_bytes());
|
||||
|
||||
/// A Ping Response
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PingResponse {
|
||||
/// Status: should be `PING_OK_RESPONSE_STATUS_TEXT`
|
||||
pub status: String,
|
||||
pub status: Bytes,
|
||||
/// Peer ID
|
||||
pub peer_id: u64,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
PingResponse,
|
||||
status: Bytes,
|
||||
peer_id: u64,
|
||||
);
|
||||
|
||||
/// A Support Flags Response
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SupportFlagsResponse {
|
||||
/// Support Flags
|
||||
pub support_flags: PeerSupportFlags,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
SupportFlagsResponse,
|
||||
support_flags: PeerSupportFlags as u32,
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
@ -101,12 +135,13 @@ mod tests {
|
|||
186, 15, 178, 70, 173, 170, 187, 31, 70, 50, 227, 11, 116, 111, 112, 95, 118, 101, 114,
|
||||
115, 105, 111, 110, 8, 1,
|
||||
];
|
||||
let handshake: HandshakeRequest = monero_epee_bin_serde::from_bytes(bytes).unwrap();
|
||||
let handshake: HandshakeRequest = epee_encoding::from_bytes(&mut &bytes[..]).unwrap();
|
||||
let basic_node_data = BasicNodeData {
|
||||
my_port: 0,
|
||||
network_id: [
|
||||
18, 48, 241, 113, 97, 4, 65, 97, 23, 49, 0, 130, 22, 161, 161, 16,
|
||||
],
|
||||
]
|
||||
.into(),
|
||||
peer_id: 9671405426614699871,
|
||||
support_flags: PeerSupportFlags::from(1_u32),
|
||||
rpc_port: 0,
|
||||
|
@ -128,9 +163,8 @@ mod tests {
|
|||
assert_eq!(basic_node_data, handshake.node_data);
|
||||
assert_eq!(core_sync_data, handshake.payload_data);
|
||||
|
||||
let encoded_bytes = monero_epee_bin_serde::to_bytes(&handshake).unwrap();
|
||||
let handshake_2: HandshakeRequest =
|
||||
monero_epee_bin_serde::from_bytes(encoded_bytes).unwrap();
|
||||
let mut encoded_bytes = epee_encoding::to_bytes(handshake.clone()).unwrap();
|
||||
let handshake_2: HandshakeRequest = epee_encoding::from_bytes(&mut encoded_bytes).unwrap();
|
||||
|
||||
assert_eq!(handshake, handshake_2);
|
||||
}
|
||||
|
@ -906,13 +940,14 @@ mod tests {
|
|||
181, 216, 193, 135, 23, 186, 168, 207, 119, 86, 235, 11, 116, 111, 112, 95, 118, 101,
|
||||
114, 115, 105, 111, 110, 8, 16,
|
||||
];
|
||||
let handshake: HandshakeResponse = monero_epee_bin_serde::from_bytes(bytes).unwrap();
|
||||
let handshake: HandshakeResponse = epee_encoding::from_bytes(&mut &bytes[..]).unwrap();
|
||||
|
||||
let basic_node_data = BasicNodeData {
|
||||
my_port: 18080,
|
||||
network_id: [
|
||||
18, 48, 241, 113, 97, 4, 65, 97, 23, 49, 0, 130, 22, 161, 161, 16,
|
||||
],
|
||||
]
|
||||
.into(),
|
||||
peer_id: 6037804360359455404,
|
||||
support_flags: PeerSupportFlags::from(1_u32),
|
||||
rpc_port: 18089,
|
||||
|
@ -935,9 +970,8 @@ mod tests {
|
|||
assert_eq!(core_sync_data, handshake.payload_data);
|
||||
assert_eq!(250, handshake.local_peerlist_new.len());
|
||||
|
||||
let encoded_bytes = monero_epee_bin_serde::to_bytes(&handshake).unwrap();
|
||||
let handshake_2: HandshakeResponse =
|
||||
monero_epee_bin_serde::from_bytes(encoded_bytes).unwrap();
|
||||
let mut encoded_bytes = epee_encoding::to_bytes(handshake.clone()).unwrap();
|
||||
let handshake_2: HandshakeResponse = epee_encoding::from_bytes(&mut encoded_bytes).unwrap();
|
||||
|
||||
assert_eq!(handshake, handshake_2);
|
||||
}
|
||||
|
|
|
@ -15,15 +15,13 @@
|
|||
|
||||
//! Common types that are used across multiple messages.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use bytes::{Buf, BufMut, Bytes};
|
||||
use epee_encoding::{epee_object, EpeeValue, InnerMarker};
|
||||
use fixed_bytes::ByteArray;
|
||||
|
||||
use crate::{
|
||||
serde_helpers::{default_false, default_zero, serde_vec_bytes},
|
||||
NetworkAddress,
|
||||
};
|
||||
use crate::NetworkAddress;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PeerSupportFlags(u32);
|
||||
|
||||
impl From<u32> for PeerSupportFlags {
|
||||
|
@ -38,6 +36,12 @@ impl From<PeerSupportFlags> for u32 {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a PeerSupportFlags> for &'a u32 {
|
||||
fn from(value: &'a PeerSupportFlags) -> Self {
|
||||
&value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PeerSupportFlags {
|
||||
//const FLUFFY_BLOCKS: u32 = 0b0000_0001;
|
||||
|
||||
|
@ -53,51 +57,65 @@ impl From<u8> for PeerSupportFlags {
|
|||
}
|
||||
|
||||
/// Basic Node Data, information on the connected peer
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BasicNodeData {
|
||||
/// Port
|
||||
pub my_port: u32,
|
||||
/// The Network Id
|
||||
pub network_id: [u8; 16],
|
||||
pub network_id: ByteArray<16>,
|
||||
/// Peer ID
|
||||
pub peer_id: u64,
|
||||
/// The Peers Support Flags
|
||||
/// (If this is not in the message the default is 0)
|
||||
#[serde(default = "default_zero")]
|
||||
pub support_flags: PeerSupportFlags,
|
||||
/// RPC Port
|
||||
/// (If this is not in the message the default is 0)
|
||||
#[serde(default = "default_zero")]
|
||||
pub rpc_port: u16,
|
||||
/// RPC Credits Per Hash
|
||||
/// (If this is not in the message the default is 0)
|
||||
#[serde(default = "default_zero")]
|
||||
pub rpc_credits_per_hash: u32,
|
||||
}
|
||||
|
||||
epee_object! {
|
||||
BasicNodeData,
|
||||
my_port: u32,
|
||||
network_id: ByteArray<16>,
|
||||
peer_id: u64,
|
||||
support_flags: PeerSupportFlags as u32 = 0_u32,
|
||||
rpc_port: u16 = 0_u16,
|
||||
rpc_credits_per_hash: u32 = 0_u32,
|
||||
}
|
||||
|
||||
/// Core Sync Data, information on the sync state of a peer
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CoreSyncData {
|
||||
/// Cumulative Difficulty Low
|
||||
/// The lower 64 bits of the 128 bit cumulative difficulty
|
||||
pub cumulative_difficulty: u64,
|
||||
/// Cumulative Difficulty High
|
||||
/// The upper 64 bits of the 128 bit cumulative difficulty
|
||||
#[serde(default = "default_zero")]
|
||||
pub cumulative_difficulty_top64: u64,
|
||||
/// Current Height of the peer
|
||||
pub current_height: u64,
|
||||
/// Pruning Seed of the peer
|
||||
/// (If this is not in the message the default is 0)
|
||||
#[serde(default = "default_zero")]
|
||||
pub pruning_seed: u32,
|
||||
/// Hash of the top block
|
||||
pub top_id: [u8; 32],
|
||||
pub top_id: ByteArray<32>,
|
||||
/// Version of the top block
|
||||
#[serde(default = "default_zero")]
|
||||
pub top_version: u8,
|
||||
}
|
||||
|
||||
epee_object! {
|
||||
CoreSyncData,
|
||||
cumulative_difficulty: u64,
|
||||
cumulative_difficulty_top64: u64 = 0_u64,
|
||||
current_height: u64,
|
||||
pruning_seed: u32 = 0_u32,
|
||||
top_id: ByteArray<32>,
|
||||
top_version: u8 = 0_u8,
|
||||
}
|
||||
|
||||
impl CoreSyncData {
|
||||
pub fn new(
|
||||
cumulative_difficulty_128: u128,
|
||||
|
@ -113,7 +131,7 @@ impl CoreSyncData {
|
|||
cumulative_difficulty_top64,
|
||||
current_height,
|
||||
pruning_seed,
|
||||
top_id,
|
||||
top_id: top_id.into(),
|
||||
top_version,
|
||||
}
|
||||
}
|
||||
|
@ -127,26 +145,32 @@ impl CoreSyncData {
|
|||
|
||||
/// PeerListEntryBase, information kept on a peer which will be entered
|
||||
/// in a peer list/store.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct PeerListEntryBase {
|
||||
/// The Peer Address
|
||||
pub adr: NetworkAddress,
|
||||
/// The Peer ID
|
||||
pub id: u64,
|
||||
/// The last Time The Peer Was Seen
|
||||
#[serde(default = "default_zero")]
|
||||
pub last_seen: i64,
|
||||
/// The Pruning Seed
|
||||
#[serde(default = "default_zero")]
|
||||
pub pruning_seed: u32,
|
||||
/// The RPC port
|
||||
#[serde(default = "default_zero")]
|
||||
pub rpc_port: u16,
|
||||
/// The RPC credits per hash
|
||||
#[serde(default = "default_zero")]
|
||||
pub rpc_credits_per_hash: u32,
|
||||
}
|
||||
|
||||
epee_object! {
|
||||
PeerListEntryBase,
|
||||
adr: NetworkAddress,
|
||||
id: u64,
|
||||
last_seen: i64 = 0_i64,
|
||||
pruning_seed: u32 = 0_u32,
|
||||
rpc_port: u16 = 0_u16,
|
||||
rpc_credits_per_hash: u32 = 0_u32,
|
||||
}
|
||||
|
||||
impl std::hash::Hash for PeerListEntryBase {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
// We only hash the adr so we can look this up in a HashSet.
|
||||
|
@ -155,22 +179,24 @@ impl std::hash::Hash for PeerListEntryBase {
|
|||
}
|
||||
|
||||
/// A pruned tx with the hash of the missing prunable data
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PrunedTxBlobEntry {
|
||||
/// The Tx
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub tx: Vec<u8>,
|
||||
pub tx: Bytes,
|
||||
/// The Prunable Tx Hash
|
||||
pub prunable_hash: [u8; 32],
|
||||
pub prunable_hash: ByteArray<32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
epee_object!(
|
||||
PrunedTxBlobEntry,
|
||||
tx: Bytes,
|
||||
prunable_hash: ByteArray<32>,
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum TransactionBlobs {
|
||||
Pruned(Vec<PrunedTxBlobEntry>),
|
||||
#[serde(with = "serde_vec_bytes")]
|
||||
Normal(Vec<Vec<u8>>),
|
||||
#[serde(skip_serializing)]
|
||||
Normal(Vec<Bytes>),
|
||||
None,
|
||||
}
|
||||
|
||||
|
@ -183,7 +209,7 @@ impl TransactionBlobs {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn take_normal(self) -> Option<Vec<Vec<u8>>> {
|
||||
pub fn take_normal(self) -> Option<Vec<Bytes>> {
|
||||
match self {
|
||||
TransactionBlobs::Normal(txs) => Some(txs),
|
||||
TransactionBlobs::Pruned(_) => None,
|
||||
|
@ -202,30 +228,57 @@ impl TransactionBlobs {
|
|||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
fn none() -> TransactionBlobs {
|
||||
TransactionBlobs::None
|
||||
}
|
||||
}
|
||||
|
||||
/// A Block that can contain transactions
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct BlockCompleteEntry {
|
||||
/// True if tx data is pruned
|
||||
#[serde(default = "default_false")]
|
||||
pub pruned: bool,
|
||||
/// The Block
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub block: Vec<u8>,
|
||||
pub block: Bytes,
|
||||
/// The Block Weight/Size
|
||||
#[serde(default = "default_zero")]
|
||||
pub block_weight: u64,
|
||||
/// The blocks txs
|
||||
#[serde(skip_serializing_if = "TransactionBlobs::is_empty")]
|
||||
#[serde(default = "TransactionBlobs::none")]
|
||||
pub txs: TransactionBlobs,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
BlockCompleteEntry,
|
||||
pruned: bool = false,
|
||||
block: Bytes,
|
||||
block_weight: u64 = 0_u64,
|
||||
txs: TransactionBlobs = TransactionBlobs::None => tx_blob_read, tx_blob_write, should_write_tx_blobs,
|
||||
);
|
||||
|
||||
fn tx_blob_read<B: Buf>(b: &mut B) -> epee_encoding::Result<TransactionBlobs> {
|
||||
let marker = epee_encoding::read_marker(b)?;
|
||||
match marker.inner_marker {
|
||||
InnerMarker::Object => Ok(TransactionBlobs::Pruned(Vec::read(b, &marker)?)),
|
||||
InnerMarker::String => Ok(TransactionBlobs::Normal(Vec::read(b, &marker)?)),
|
||||
_ => Err(epee_encoding::Error::Value("Invalid marker for tx blobs")),
|
||||
}
|
||||
}
|
||||
|
||||
fn tx_blob_write<B: BufMut>(
|
||||
val: TransactionBlobs,
|
||||
field_name: &str,
|
||||
w: &mut B,
|
||||
) -> epee_encoding::Result<()> {
|
||||
if should_write_tx_blobs(&val) {
|
||||
match val {
|
||||
TransactionBlobs::Normal(bytes) => epee_encoding::write_field(bytes, field_name, w)?,
|
||||
TransactionBlobs::Pruned(obj) => epee_encoding::write_field(obj, field_name, w)?,
|
||||
TransactionBlobs::None => (),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn should_write_tx_blobs(val: &TransactionBlobs) -> bool {
|
||||
!val.is_empty()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
|
|
@ -18,16 +18,15 @@
|
|||
//! Protocol message requests don't have to be responded to in order unlike
|
||||
//! admin messages.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_bytes::ByteBuf;
|
||||
use bytes::Bytes;
|
||||
|
||||
use monero_epee_bin_serde::container_as_blob;
|
||||
use epee_encoding::{container_as_blob::ContainerAsBlob, epee_object};
|
||||
use fixed_bytes::{ByteArray, ByteArrayVec};
|
||||
|
||||
use super::common::BlockCompleteEntry;
|
||||
use crate::serde_helpers::*;
|
||||
|
||||
/// A block that SHOULD have transactions
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NewBlock {
|
||||
/// Block with txs
|
||||
pub b: BlockCompleteEntry,
|
||||
|
@ -35,63 +34,80 @@ pub struct NewBlock {
|
|||
pub current_blockchain_height: u64,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
NewBlock,
|
||||
b: BlockCompleteEntry,
|
||||
current_blockchain_height: u64,
|
||||
);
|
||||
|
||||
/// New Tx Pool Transactions
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NewTransactions {
|
||||
/// Tx Blobs
|
||||
pub txs: Vec<ByteBuf>,
|
||||
pub txs: Vec<Bytes>,
|
||||
/// Dandelionpp true if fluff - backwards compatible mode is fluff
|
||||
#[serde(default = "default_true")]
|
||||
pub dandelionpp_fluff: bool,
|
||||
/// Padding
|
||||
#[serde(rename = "_")]
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub padding: Vec<u8>,
|
||||
pub padding: Bytes,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
NewTransactions,
|
||||
txs: Vec<Bytes>,
|
||||
dandelionpp_fluff: bool = true,
|
||||
padding("_"): Bytes,
|
||||
);
|
||||
|
||||
/// A Request For Blocks
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct GetObjectsRequest {
|
||||
/// Block hashes we want
|
||||
#[serde(default = "Vec::new")]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(with = "container_as_blob")]
|
||||
pub blocks: Vec<[u8; 32]>,
|
||||
pub blocks: ByteArrayVec<32>,
|
||||
/// Pruned
|
||||
#[serde(default = "default_false")]
|
||||
pub pruned: bool,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
GetObjectsRequest,
|
||||
blocks: ByteArrayVec<32>,
|
||||
pruned: bool = false,
|
||||
);
|
||||
|
||||
/// A Blocks Response
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct GetObjectsResponse {
|
||||
/// Blocks
|
||||
// We dont need to give this a default value as there always is at least 1 block
|
||||
pub blocks: Vec<BlockCompleteEntry>,
|
||||
/// Missed IDs
|
||||
#[serde(default = "Vec::new")]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(with = "container_as_blob")]
|
||||
pub missed_ids: Vec<[u8; 32]>,
|
||||
pub missed_ids: ByteArrayVec<32>,
|
||||
/// The height of the peers blockchain
|
||||
pub current_blockchain_height: u64,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
GetObjectsResponse,
|
||||
blocks: Vec<BlockCompleteEntry>,
|
||||
missed_ids: ByteArrayVec<32>,
|
||||
current_blockchain_height: u64,
|
||||
);
|
||||
|
||||
/// A Chain Request
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ChainRequest {
|
||||
/// Block IDs
|
||||
#[serde(default = "Vec::new")]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(with = "container_as_blob")]
|
||||
pub block_ids: Vec<[u8; 32]>,
|
||||
pub block_ids: ByteArrayVec<32>,
|
||||
/// Prune
|
||||
#[serde(default = "default_false")]
|
||||
pub prune: bool,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
ChainRequest,
|
||||
block_ids: ByteArrayVec<32>,
|
||||
prune: bool = false,
|
||||
);
|
||||
|
||||
/// A Chain Response
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ChainResponse {
|
||||
/// Start Height
|
||||
pub start_height: u64,
|
||||
|
@ -100,26 +116,28 @@ pub struct ChainResponse {
|
|||
/// Cumulative Difficulty Low
|
||||
pub cumulative_difficulty: u64,
|
||||
/// Cumulative Difficulty High
|
||||
#[serde(default = "default_zero")]
|
||||
pub cumulative_difficulty_top64: u64,
|
||||
/// Block IDs
|
||||
#[serde(default = "Vec::new")]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(with = "container_as_blob")]
|
||||
pub m_block_ids: Vec<[u8; 32]>,
|
||||
pub m_block_ids: ByteArrayVec<32>,
|
||||
/// Block Weights
|
||||
#[serde(default = "Vec::new")]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(with = "container_as_blob")]
|
||||
pub m_block_weights: Vec<u64>,
|
||||
/// The first Block in the response
|
||||
#[serde(default = "Vec::new")]
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub first_block: Vec<u8>,
|
||||
pub first_block: Bytes,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
ChainResponse,
|
||||
start_height: u64,
|
||||
total_height: u64,
|
||||
cumulative_difficulty: u64,
|
||||
cumulative_difficulty_top64: u64 = 0_u64,
|
||||
m_block_ids: ByteArrayVec<32>,
|
||||
m_block_weights: Vec<u64> as ContainerAsBlob<u64>,
|
||||
first_block: Bytes,
|
||||
);
|
||||
|
||||
/// A Block that doesn't have transactions unless requested
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NewFluffyBlock {
|
||||
/// Block which might have transactions
|
||||
pub b: BlockCompleteEntry,
|
||||
|
@ -127,30 +145,42 @@ pub struct NewFluffyBlock {
|
|||
pub current_blockchain_height: u64,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
NewFluffyBlock,
|
||||
b: BlockCompleteEntry,
|
||||
current_blockchain_height: u64,
|
||||
);
|
||||
|
||||
/// A request for Txs we are missing from our TxPool
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FluffyMissingTransactionsRequest {
|
||||
/// The Block we are missing the Txs in
|
||||
pub block_hash: [u8; 32],
|
||||
pub block_hash: ByteArray<32>,
|
||||
/// The current blockchain height
|
||||
pub current_blockchain_height: u64,
|
||||
/// The Tx Indices
|
||||
#[serde(default = "Vec::new")]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(with = "container_as_blob")]
|
||||
pub missing_tx_indices: Vec<u64>,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
FluffyMissingTransactionsRequest,
|
||||
block_hash: ByteArray<32>,
|
||||
current_blockchain_height: u64,
|
||||
missing_tx_indices: Vec<u64> as ContainerAsBlob<u64>,
|
||||
);
|
||||
|
||||
/// TxPoolCompliment
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct GetTxPoolCompliment {
|
||||
/// Tx Hashes
|
||||
#[serde(default = "Vec::new")]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(with = "container_as_blob")]
|
||||
pub hashes: Vec<[u8; 32]>,
|
||||
pub hashes: ByteArrayVec<32>,
|
||||
}
|
||||
|
||||
epee_object!(
|
||||
GetTxPoolCompliment,
|
||||
hashes: ByteArrayVec<32>,
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
@ -667,13 +697,13 @@ mod tests {
|
|||
248, 248, 91, 110, 107, 144, 12, 175, 253, 21, 121, 28,
|
||||
];
|
||||
|
||||
let new_transactions: NewTransactions = monero_epee_bin_serde::from_bytes(bytes).unwrap();
|
||||
let new_transactions: NewTransactions = epee_encoding::from_bytes(&mut &bytes[..]).unwrap();
|
||||
|
||||
assert_eq!(4, new_transactions.txs.len());
|
||||
|
||||
let encoded_bytes = monero_epee_bin_serde::to_bytes(&new_transactions).unwrap();
|
||||
let mut encoded_bytes = epee_encoding::to_bytes(new_transactions.clone()).unwrap();
|
||||
let new_transactions_2: NewTransactions =
|
||||
monero_epee_bin_serde::from_bytes(encoded_bytes).unwrap();
|
||||
epee_encoding::from_bytes(&mut encoded_bytes).unwrap();
|
||||
|
||||
assert_eq!(new_transactions, new_transactions_2);
|
||||
}
|
||||
|
@ -1019,11 +1049,10 @@ mod tests {
|
|||
101, 110, 116, 95, 98, 108, 111, 99, 107, 99, 104, 97, 105, 110, 95, 104, 101, 105,
|
||||
103, 104, 116, 5, 209, 45, 42, 0, 0, 0, 0, 0,
|
||||
];
|
||||
let fluffy_block: NewFluffyBlock = monero_epee_bin_serde::from_bytes(bytes).unwrap();
|
||||
let fluffy_block: NewFluffyBlock = epee_encoding::from_bytes(&mut &bytes[..]).unwrap();
|
||||
|
||||
let encoded_bytes = monero_epee_bin_serde::to_bytes(&fluffy_block).unwrap();
|
||||
let fluffy_block_2: NewFluffyBlock =
|
||||
monero_epee_bin_serde::from_bytes(encoded_bytes).unwrap();
|
||||
let mut encoded_bytes = epee_encoding::to_bytes(fluffy_block.clone()).unwrap();
|
||||
let fluffy_block_2: NewFluffyBlock = epee_encoding::from_bytes(&mut encoded_bytes).unwrap();
|
||||
|
||||
assert_eq!(fluffy_block, fluffy_block_2);
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
pub(crate) fn default_false() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn default_zero<T: TryFrom<u8>>() -> T {
|
||||
0.try_into()
|
||||
.map_err(|_| "Couldn't fit 0 into integer type!")
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(crate) mod serde_vec_bytes {
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
use serde_bytes::ByteBuf;
|
||||
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<Vec<Vec<u8>>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Vec::<ByteBuf>::deserialize(d)?
|
||||
.into_iter()
|
||||
.map(ByteBuf::into_vec)
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn serialize<S>(t: &[Vec<u8>], s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
s.collect_seq(t.iter())
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ async fn handshake_cuprate_to_cuprate() {
|
|||
|
||||
let our_basic_node_data_1 = BasicNodeData {
|
||||
my_port: 0,
|
||||
network_id: Network::Mainnet.network_id(),
|
||||
network_id: Network::Mainnet.network_id().into(),
|
||||
peer_id: 87980,
|
||||
// TODO: This fails if the support flags are empty (0)
|
||||
support_flags: PeerSupportFlags::from(1_u32),
|
||||
|
@ -113,7 +113,7 @@ async fn handshake() {
|
|||
|
||||
let our_basic_node_data = BasicNodeData {
|
||||
my_port: 0,
|
||||
network_id: Network::Mainnet.network_id(),
|
||||
network_id: Network::Mainnet.network_id().into(),
|
||||
peer_id: 87980,
|
||||
support_flags: PeerSupportFlags::from(1_u32),
|
||||
rpc_port: 0,
|
||||
|
|
Loading…
Reference in a new issue