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",
|
"curve25519-dalek",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
"epee-encoding",
|
||||||
"futures",
|
"futures",
|
||||||
"hex",
|
"hex",
|
||||||
"monero-consensus",
|
"monero-consensus",
|
||||||
"monero-epee-bin-serde",
|
|
||||||
"monero-serai",
|
"monero-serai",
|
||||||
"monero-wire",
|
"monero-wire",
|
||||||
"multiexp",
|
"multiexp",
|
||||||
|
@ -604,6 +604,7 @@ dependencies = [
|
||||||
"fixed-bytes",
|
"fixed-bytes",
|
||||||
"hex",
|
"hex",
|
||||||
"paste",
|
"paste",
|
||||||
|
"ref-cast",
|
||||||
"sealed",
|
"sealed",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
@ -1142,16 +1143,6 @@ dependencies = [
|
||||||
"tracing",
|
"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]]
|
[[package]]
|
||||||
name = "monero-generators"
|
name = "monero-generators"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -1232,11 +1223,11 @@ dependencies = [
|
||||||
name = "monero-wire"
|
name = "monero-wire"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"epee-encoding",
|
||||||
|
"fixed-bytes",
|
||||||
"hex",
|
"hex",
|
||||||
"levin-cuprate",
|
"levin-cuprate",
|
||||||
"monero-epee-bin-serde",
|
|
||||||
"serde",
|
|
||||||
"serde_bytes",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1587,6 +1578,26 @@ dependencies = [
|
||||||
"thiserror",
|
"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]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
@ -1770,15 +1781,6 @@ dependencies = [
|
||||||
"serde_derive",
|
"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]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.195"
|
version = "1.0.195"
|
||||||
|
|
|
@ -21,7 +21,7 @@ binaries = [
|
||||||
"dep:tracing-subscriber",
|
"dep:tracing-subscriber",
|
||||||
"dep:serde_json",
|
"dep:serde_json",
|
||||||
"dep:serde",
|
"dep:serde",
|
||||||
"dep:monero-epee-bin-serde",
|
"dep:epee-encoding",
|
||||||
"dep:monero-wire",
|
"dep:monero-wire",
|
||||||
"dep:borsh",
|
"dep:borsh",
|
||||||
"dep:dirs",
|
"dep:dirs",
|
||||||
|
@ -51,7 +51,7 @@ hex = "0.4"
|
||||||
|
|
||||||
# used in binaries
|
# used in binaries
|
||||||
monero-wire = {path="../net/monero-wire", optional = true}
|
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_json = {version = "1", optional = true}
|
||||||
serde = {version = "1", optional = true, features = ["derive"]}
|
serde = {version = "1", optional = true, features = ["derive"]}
|
||||||
tracing-subscriber = {version = "0.3", optional = true}
|
tracing-subscriber = {version = "0.3", optional = true}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::ops::Deref;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
ops::Range,
|
ops::Range,
|
||||||
|
@ -15,9 +16,9 @@ use monero_serai::{
|
||||||
rpc::{HttpRpc, Rpc},
|
rpc::{HttpRpc, Rpc},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use monero_wire::common::{BlockCompleteEntry, TransactionBlobs};
|
use monero_wire::common::TransactionBlobs;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::RwLock,
|
sync::RwLock,
|
||||||
|
@ -174,41 +175,56 @@ impl RpcConnection {
|
||||||
) -> Result<Vec<(Block, Vec<Transaction>)>, tower::BoxError> {
|
) -> Result<Vec<(Block, Vec<Transaction>)>, tower::BoxError> {
|
||||||
tracing::info!("Getting blocks in range: {:?}", range);
|
tracing::info!("Getting blocks in range: {:?}", range);
|
||||||
|
|
||||||
#[derive(Serialize)]
|
mod items {
|
||||||
|
use monero_wire::common::BlockCompleteEntry;
|
||||||
|
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
pub heights: Vec<u64>,
|
pub heights: Vec<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
epee_encoding::epee_object!(
|
||||||
|
Request,
|
||||||
|
heights: Vec<u64>,
|
||||||
|
);
|
||||||
|
|
||||||
pub struct Response {
|
pub struct Response {
|
||||||
pub blocks: Vec<BlockCompleteEntry>,
|
pub blocks: Vec<BlockCompleteEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_encoding::epee_object!(
|
||||||
|
Response,
|
||||||
|
blocks: Vec<BlockCompleteEntry>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
use items::*;
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.con
|
.con
|
||||||
.bin_call(
|
.bin_call(
|
||||||
"get_blocks_by_height.bin",
|
"get_blocks_by_height.bin",
|
||||||
monero_epee_bin_serde::to_bytes(&Request {
|
epee_encoding::to_bytes(Request {
|
||||||
heights: range.collect(),
|
heights: range.collect(),
|
||||||
})?,
|
})?
|
||||||
|
.to_vec(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let address = self.address.clone();
|
let address = self.address.clone();
|
||||||
rayon_spawn_async(move || {
|
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
|
||||||
.blocks
|
.blocks
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(|b| {
|
.map(|b| {
|
||||||
let block = Block::read(&mut b.block.as_slice())?;
|
let block = Block::read(&mut b.block.deref())?;
|
||||||
|
|
||||||
let txs = match b.txs {
|
let txs = match b.txs {
|
||||||
TransactionBlobs::Pruned(_) => return Err("node sent pruned txs!".into()),
|
TransactionBlobs::Pruned(_) => return Err("node sent pruned txs!".into()),
|
||||||
TransactionBlobs::Normal(txs) => txs
|
TransactionBlobs::Normal(txs) => txs
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(|tx| Transaction::read(&mut tx.as_slice()))
|
.map(|tx| Transaction::read(&mut tx.deref()))
|
||||||
.collect::<Result<_, _>>()?,
|
.collect::<Result<_, _>>()?,
|
||||||
TransactionBlobs::None => vec![],
|
TransactionBlobs::None => vec![],
|
||||||
};
|
};
|
||||||
|
@ -237,30 +253,57 @@ impl RpcConnection {
|
||||||
out_ids.values().map(|amt_map| amt_map.len()).sum::<usize>()
|
out_ids.values().map(|amt_map| amt_map.len()).sum::<usize>()
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Serialize, Copy, Clone)]
|
mod items {
|
||||||
struct OutputID {
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct OutputID {
|
||||||
|
pub amount: u64,
|
||||||
|
pub index: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
epee_encoding::epee_object!(
|
||||||
|
OutputID,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
index: u64,
|
index: u64,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Request {
|
||||||
|
pub outputs: Vec<OutputID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Clone)]
|
epee_encoding::epee_object!(
|
||||||
struct Request<'a> {
|
Request,
|
||||||
outputs: &'a [OutputID],
|
outputs: Vec<OutputID>,
|
||||||
|
);
|
||||||
|
|
||||||
|
pub struct OutputRes {
|
||||||
|
pub height: u64,
|
||||||
|
pub key: [u8; 32],
|
||||||
|
pub mask: [u8; 32],
|
||||||
|
pub txid: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
epee_encoding::epee_object!(
|
||||||
struct OutputRes {
|
OutputRes,
|
||||||
height: u64,
|
height: u64,
|
||||||
key: [u8; 32],
|
key: [u8; 32],
|
||||||
mask: [u8; 32],
|
mask: [u8; 32],
|
||||||
txid: [u8; 32],
|
txid: [u8; 32],
|
||||||
|
);
|
||||||
|
|
||||||
|
pub struct Response {
|
||||||
|
pub outs: Vec<OutputRes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
epee_encoding::epee_object!(
|
||||||
struct Response {
|
Response,
|
||||||
outs: Vec<OutputRes>,
|
outs: Vec<OutputRes>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use items::*;
|
||||||
|
|
||||||
let outputs = rayon_spawn_async(|| {
|
let outputs = rayon_spawn_async(|| {
|
||||||
out_ids
|
out_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -281,7 +324,10 @@ impl RpcConnection {
|
||||||
.con
|
.con
|
||||||
.bin_call(
|
.bin_call(
|
||||||
"get_outs.bin",
|
"get_outs.bin",
|
||||||
monero_epee_bin_serde::to_bytes(&Request { outputs: &outputs })?,
|
epee_encoding::to_bytes(Request {
|
||||||
|
outputs: outputs.clone(),
|
||||||
|
})?
|
||||||
|
.to_vec(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -289,7 +335,8 @@ impl RpcConnection {
|
||||||
|
|
||||||
let span = tracing::Span::current();
|
let span = tracing::Span::current();
|
||||||
rayon_spawn_async(move || {
|
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());
|
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"
|
sealed = "0.5.0"
|
||||||
paste = "1.0.14"
|
paste = "1.0.14"
|
||||||
|
ref-cast = "1.0.22"
|
||||||
bytes = { workspace = true }
|
bytes = { workspace = true }
|
||||||
thiserror = { workspace = true, optional = 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 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();
|
//! 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 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();
|
//! let data = to_bytes(val).unwrap();
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use core::{ops::Deref, str::from_utf8 as str_from_utf8};
|
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;
|
pub mod error;
|
||||||
mod io;
|
mod io;
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
|
@ -139,13 +139,13 @@ pub trait EpeeObject: Sized {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the object `T` from a byte array.
|
/// Read the object `T` from a byte array.
|
||||||
pub fn from_bytes<T: EpeeObject>(mut buf: &[u8]) -> Result<T> {
|
pub fn from_bytes<T: EpeeObject, B: Buf>(buf: &mut B) -> Result<T> {
|
||||||
read_head_object(&mut buf)
|
read_head_object(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn the object into epee bytes.
|
/// Turn the object into epee bytes.
|
||||||
pub fn to_bytes<T: EpeeObject>(val: T) -> Result<Vec<u8>> {
|
pub fn to_bytes<T: EpeeObject>(val: T) -> Result<BytesMut> {
|
||||||
let mut buf = Vec::<u8>::new();
|
let mut buf = BytesMut::new();
|
||||||
write_head_object(val, &mut buf)?;
|
write_head_object(val, &mut buf)?;
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ pub use paste::paste;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! field_name {
|
macro_rules! field_name {
|
||||||
($field: ident, $alt_name: literal) => {
|
($field: tt, $alt_name: tt) => {
|
||||||
$alt_name
|
$alt_name
|
||||||
};
|
};
|
||||||
($field: ident,) => {
|
($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_export]
|
||||||
macro_rules! epee_object {
|
macro_rules! epee_object {
|
||||||
(
|
(
|
||||||
$obj:ident,
|
$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 ,)+)?
|
$(!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> {
|
fn add_field<B: epee_encoding::macros::bytes::Buf>(&mut self, name: &str, b: &mut B) -> epee_encoding::error::Result<bool> {
|
||||||
match name {
|
match name {
|
||||||
$(epee_encoding::field_name!($field, $($alt_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"))?;
|
Err(epee_encoding::error::Error::Value("Duplicate field in data"))?;
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
@ -65,11 +77,18 @@ macro_rules! epee_object {
|
||||||
Ok(
|
Ok(
|
||||||
$obj {
|
$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(Some($default)))?
|
||||||
.or(epee_encoding::EpeeValue::epee_default_value())
|
.or(epee_default_value)
|
||||||
$(.map(<$ty_as>::into))?
|
$(.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;
|
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;
|
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<()> {
|
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) ) {
|
let field = epee_encoding::try_right_then_left!(self.$field, $(<$ty_as>::from(self.$field))? );
|
||||||
epee_encoding::write_field($(<$ty_as>::from)?(self.$field), epee_encoding::field_name!($field, $($alt_name)?), w)?;
|
|
||||||
|
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.
|
/// the different possible base epee values.
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use bytes::{Buf, BufMut, Bytes};
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
use sealed::sealed;
|
use sealed::sealed;
|
||||||
|
|
||||||
use fixed_bytes::{ByteArray, ByteArrayVec};
|
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
|
/// 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
|
/// defined in the lib, to make an [`EpeeValue`] outside the lib you will need to
|
||||||
/// use the trait [`EpeeObject`].
|
/// use the trait [`EpeeObject`].
|
||||||
#[sealed]
|
#[sealed(pub(crate))]
|
||||||
pub trait EpeeValue: Sized {
|
pub trait EpeeValue: Sized {
|
||||||
const MARKER: Marker;
|
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]
|
#[sealed::sealed]
|
||||||
impl<const N: usize> EpeeValue for ByteArrayVec<N> {
|
impl<const N: usize> EpeeValue for ByteArrayVec<N> {
|
||||||
const MARKER: Marker = Marker::new(InnerMarker::String);
|
const MARKER: Marker = Marker::new(InnerMarker::String);
|
||||||
|
@ -481,6 +517,8 @@ epee_seq!(f64);
|
||||||
epee_seq!(bool);
|
epee_seq!(bool);
|
||||||
epee_seq!(Vec<u8>);
|
epee_seq!(Vec<u8>);
|
||||||
epee_seq!(String);
|
epee_seq!(String);
|
||||||
|
epee_seq!(Bytes);
|
||||||
|
epee_seq!(BytesMut);
|
||||||
|
|
||||||
#[sealed]
|
#[sealed]
|
||||||
impl<T: EpeeValue> EpeeValue for Option<T> {
|
impl<T: EpeeValue> EpeeValue for Option<T> {
|
||||||
|
|
|
@ -27,7 +27,7 @@ fn epee_alt_name() {
|
||||||
let val2 = AltName2 { val2: 40, d: 30 };
|
let val2 = AltName2 { val2: 40, d: 30 };
|
||||||
let bytes = to_bytes(val2).unwrap();
|
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();
|
let bytes2 = to_bytes(val).unwrap();
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ fn duplicate_key() {
|
||||||
b'a', 0x0B, 0x00,
|
b'a', 0x0B, 0x00,
|
||||||
];
|
];
|
||||||
|
|
||||||
assert!(from_bytes::<T>(&data).is_err());
|
assert!(from_bytes::<T, _>(&mut &data[..]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -35,5 +35,5 @@ fn duplicate_key_with_default() {
|
||||||
b'a', 0x0B, 0x00,
|
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!(
|
epee_object!(
|
||||||
Optional,
|
Optional,
|
||||||
val: u8,
|
val: u8,
|
||||||
optional_val: i32 = -4,
|
optional_val: i32 = -4_i32,
|
||||||
);
|
);
|
||||||
pub struct NotOptional {
|
pub struct NotOptional {
|
||||||
val: u8,
|
val: u8,
|
||||||
|
@ -37,11 +37,11 @@ fn epee_default_does_not_encode() {
|
||||||
val: 1,
|
val: 1,
|
||||||
optional_val: -4,
|
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.optional_val, -4);
|
||||||
assert_eq!(val.val, 1);
|
assert_eq!(val.val, 1);
|
||||||
}
|
}
|
||||||
|
@ -52,11 +52,11 @@ fn epee_non_default_does_encode() {
|
||||||
val: 8,
|
val: 8,
|
||||||
optional_val: -3,
|
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.optional_val, -3);
|
||||||
assert_eq!(val.val, 8)
|
assert_eq!(val.val, 8)
|
||||||
}
|
}
|
||||||
|
@ -64,11 +64,11 @@ fn epee_non_default_does_encode() {
|
||||||
#[test]
|
#[test]
|
||||||
fn epee_value_not_present_with_default() {
|
fn epee_value_not_present_with_default() {
|
||||||
let val = NotPresent { val: 76 };
|
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.optional_val, -4);
|
||||||
assert_eq!(val.val, 76)
|
assert_eq!(val.val, 76)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,9 +43,9 @@ fn epee_flatten() {
|
||||||
val: 94,
|
val: 94,
|
||||||
val2: vec![4, 5],
|
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.val2, val2.val2);
|
||||||
assert_eq!(val.child.val, val2.val);
|
assert_eq!(val.child.val, val2.val);
|
||||||
|
@ -95,8 +95,8 @@ epee_object!(
|
||||||
fn epee_double_flatten() {
|
fn epee_double_flatten() {
|
||||||
let val = Parent12::default();
|
let val = Parent12::default();
|
||||||
|
|
||||||
let bytes = to_bytes(val.clone()).unwrap();
|
let mut bytes = to_bytes(val.clone()).unwrap();
|
||||||
let val1: Parent12 = from_bytes(&bytes).unwrap();
|
let val1: Parent12 = from_bytes(&mut bytes).unwrap();
|
||||||
|
|
||||||
assert_eq!(val, val1);
|
assert_eq!(val, val1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use epee_encoding::{epee_object, from_bytes, to_bytes};
|
use epee_encoding::{epee_object, from_bytes, to_bytes};
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct T {
|
struct T {
|
||||||
|
@ -11,9 +12,10 @@ epee_object!(
|
||||||
);
|
);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::useless_asref)]
|
||||||
fn optional_val_not_in_data() {
|
fn optional_val_not_in_data() {
|
||||||
let bytes: &[u8] = b"\x01\x11\x01\x01\x01\x01\x02\x01\x01\x00";
|
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();
|
let bytes2 = to_bytes(t.clone()).unwrap();
|
||||||
assert_eq!(bytes, bytes2);
|
assert_eq!(bytes, bytes2);
|
||||||
assert!(t.val.is_none());
|
assert!(t.val.is_none());
|
||||||
|
@ -24,8 +26,8 @@ fn optional_val_in_data() {
|
||||||
let bytes = [
|
let bytes = [
|
||||||
0x01, 0x11, 0x01, 0x1, 0x01, 0x01, 0x02, 0x1, 0x1, 0x04, 0x03, b'v', b'a', b'l', 0x08, 21,
|
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();
|
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);
|
assert_eq!(t.val.unwrap(), 21);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,9 @@ epee_object!(
|
||||||
fn p2p_handshake() {
|
fn p2p_handshake() {
|
||||||
let bytes = hex::decode("01110101010102010108096e6f64655f646174610c10076d795f706f727406a04600000a6e6574776f726b5f69640a401230f171610441611731008216a1a11007706565725f6964053eb3c096c4471c340d737570706f72745f666c61677306010000000c7061796c6f61645f646174610c181563756d756c61746976655f646966666963756c7479053951f7a79aab4a031b63756d756c61746976655f646966666963756c74795f746f7036340500000000000000000e63757272656e745f68656967687405fa092a00000000000c7072756e696e675f73656564068001000006746f705f69640a806cc497b230ba57a95edb370be8d6870c94e0992937c89b1def3a4cb7726d37ad0b746f705f76657273696f6e0810").unwrap();
|
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]
|
#[test]
|
||||||
fn rpc_get_outs_response() {
|
fn rpc_get_outs_response() {
|
||||||
let bytes = hex::decode("011101010101020101140763726564697473050000000000000000046f7574738c04140668656967687405a100000000000000036b65790a802d392d0be38eb4699c17767e62a063b8d2f989ec15c80e5d2665ab06f8397439046d61736b0a805e8b863c5b267deda13f4bc5d5ec8e59043028380f2431bc8691c15c83e1fea404747869640a80c0646e065a33b849f0d9563673ca48eb0c603fe721dd982720dba463172c246f08756e6c6f636b65640b00067374617475730a084f4b08746f705f686173680a0009756e747275737465640b00").unwrap();
|
let bytes = hex::decode("011101010101020101140763726564697473050000000000000000046f7574738c04140668656967687405a100000000000000036b65790a802d392d0be38eb4699c17767e62a063b8d2f989ec15c80e5d2665ab06f8397439046d61736b0a805e8b863c5b267deda13f4bc5d5ec8e59043028380f2431bc8691c15c83e1fea404747869640a80c0646e065a33b849f0d9563673ca48eb0c603fe721dd982720dba463172c246f08756e6c6f636b65640b00067374617475730a084f4b08746f705f686173680a0009756e747275737465640b00").unwrap();
|
||||||
let val: GetOutsResponse = from_bytes(&bytes).unwrap();
|
let val: GetOutsResponse = 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_out_indexes_response() {
|
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,
|
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,
|
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,
|
104, 10, 0, 9, 117, 110, 116, 114, 117, 115, 116, 101, 100, 11, 0,
|
||||||
];
|
];
|
||||||
let val: GetOIndexesResponse = from_bytes(&bytes).unwrap();
|
let val: GetOIndexesResponse = 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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,9 @@ fn seq_with_zero_len_can_have_any_marker() {
|
||||||
data.push(0x80 | marker);
|
data.push(0x80 | marker);
|
||||||
data.push(0);
|
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..);
|
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.push(0x04); // varint length of 1
|
||||||
data.extend_from_slice(&1_i64.to_le_bytes());
|
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..);
|
data.drain(14..);
|
||||||
}
|
}
|
||||||
|
@ -56,5 +56,5 @@ fn seq_with_non_zero_len_must_have_correct_marker() {
|
||||||
data.push(0x80 + 1);
|
data.push(0x80 + 1);
|
||||||
data.push(0x04); // varint length
|
data.push(0x04); // varint length
|
||||||
data.extend_from_slice(&1_i64.to_le_bytes());
|
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,
|
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())
|
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`].
|
/// 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]`.
|
/// This implements [`Deref`] with the target being `[u8; N]`.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct ByteArray<const N: usize>(Bytes);
|
pub struct ByteArray<const N: usize>(Bytes);
|
||||||
|
|
||||||
impl<const N: usize> ByteArray<N> {
|
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> {
|
impl<const N: usize> Deref for ByteArray<N> {
|
||||||
type Target = [u8; 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);
|
pub struct ByteArrayVec<const N: usize>(Bytes);
|
||||||
|
|
||||||
impl<const N: usize> ByteArrayVec<N> {
|
impl<const N: usize> ByteArrayVec<N> {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use bytes::{Buf, BufMut, BytesMut};
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
use tokio_util::codec::{Decoder, Encoder};
|
use tokio_util::codec::{Decoder, Encoder};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -111,7 +111,7 @@ impl<C: LevinCommand> Decoder for LevinBucketCodec<C> {
|
||||||
|
|
||||||
return Ok(Some(Bucket {
|
return Ok(Some(Bucket {
|
||||||
header,
|
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> {
|
enum MessageState<C> {
|
||||||
#[default]
|
#[default]
|
||||||
WaitingForBucket,
|
WaitingForBucket,
|
||||||
WaitingForRestOfFragment(Vec<u8>, MessageType, C),
|
WaitingForRestOfFragment(Vec<Bytes>, MessageType, C),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A tokio-codec for levin messages or in other words the decoded body
|
/// 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 {
|
loop {
|
||||||
match &mut self.state {
|
match &mut self.state {
|
||||||
MessageState::WaitingForBucket => {
|
MessageState::WaitingForBucket => {
|
||||||
let Some(bucket) = self.bucket_codec.decode(src)? else {
|
let Some(mut bucket) = self.bucket_codec.decode(src)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ impl<T: LevinBody> Decoder for LevinMessageCodec<T> {
|
||||||
let _ = std::mem::replace(
|
let _ = std::mem::replace(
|
||||||
&mut self.state,
|
&mut self.state,
|
||||||
MessageState::WaitingForRestOfFragment(
|
MessageState::WaitingForRestOfFragment(
|
||||||
bucket.body.to_vec(),
|
vec![bucket.body],
|
||||||
message_type,
|
message_type,
|
||||||
bucket.header.command,
|
bucket.header.command,
|
||||||
),
|
),
|
||||||
|
@ -209,7 +209,7 @@ impl<T: LevinBody> Decoder for LevinMessageCodec<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Some(T::decode_message(
|
return Ok(Some(T::decode_message(
|
||||||
&bucket.body,
|
&mut bucket.body,
|
||||||
message_type,
|
message_type,
|
||||||
bucket.header.command,
|
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() {
|
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)
|
std::mem::replace(&mut self.state, MessageState::WaitingForBucket)
|
||||||
else {
|
else {
|
||||||
unreachable!();
|
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 std::fmt::Debug;
|
||||||
|
|
||||||
|
use bytes::{Buf, Bytes};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
const MONERO_PROTOCOL_VERSION: u32 = 1;
|
const MONERO_PROTOCOL_VERSION: u32 = 1;
|
||||||
|
@ -102,7 +103,7 @@ pub struct Bucket<C> {
|
||||||
/// The bucket header
|
/// The bucket header
|
||||||
pub header: BucketHead<C>,
|
pub header: BucketHead<C>,
|
||||||
/// The bucket body
|
/// The bucket body
|
||||||
pub body: Vec<u8>,
|
pub body: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An enum representing if the message is a request, response or notification.
|
/// An enum representing if the message is a request, response or notification.
|
||||||
|
@ -158,7 +159,7 @@ pub struct BucketBuilder<C> {
|
||||||
command: Option<C>,
|
command: Option<C>,
|
||||||
return_code: Option<i32>,
|
return_code: Option<i32>,
|
||||||
protocol_version: Option<u32>,
|
protocol_version: Option<u32>,
|
||||||
body: Option<Vec<u8>>,
|
body: Option<Bytes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> Default for BucketBuilder<C> {
|
impl<C> Default for BucketBuilder<C> {
|
||||||
|
@ -195,7 +196,7 @@ impl<C: LevinCommand> BucketBuilder<C> {
|
||||||
self.protocol_version = Some(version)
|
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)
|
self.body = Some(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,14 +223,14 @@ pub trait LevinBody: Sized {
|
||||||
type Command: LevinCommand;
|
type Command: LevinCommand;
|
||||||
|
|
||||||
/// Decodes the message from the data in the header
|
/// Decodes the message from the data in the header
|
||||||
fn decode_message(
|
fn decode_message<B: Buf>(
|
||||||
body: &[u8],
|
body: &mut B,
|
||||||
typ: MessageType,
|
typ: MessageType,
|
||||||
command: Self::Command,
|
command: Self::Command,
|
||||||
) -> Result<Self, BucketError>;
|
) -> Result<Self, BucketError>;
|
||||||
|
|
||||||
/// Encodes the message
|
/// 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.
|
/// The levin commands.
|
||||||
|
|
|
@ -9,9 +9,10 @@ repository = "https://github.com/SyntheticBird45/cuprate/tree/main/net/monero-wi
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
levin-cuprate = {path="../levin"}
|
levin-cuprate = {path="../levin"}
|
||||||
monero-epee-bin-serde = { workspace = true, features = ["container_as_blob"] }
|
epee-encoding = { path = "../epee-encoding" }
|
||||||
serde = { workspace = true, features = ["derive", "std"]}
|
fixed-bytes = { path = "../fixed-bytes" }
|
||||||
serde_bytes = { workspace = true, features = ["std"]}
|
|
||||||
|
bytes = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
|
|
||||||
pub mod network_address;
|
pub mod network_address;
|
||||||
pub mod p2p;
|
pub mod p2p;
|
||||||
mod serde_helpers;
|
|
||||||
|
|
||||||
pub use levin_cuprate::BucketError;
|
pub use levin_cuprate::BucketError;
|
||||||
pub use network_address::{NetZone, NetworkAddress};
|
pub use network_address::{NetZone, NetworkAddress};
|
||||||
|
|
|
@ -17,10 +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 bytes::BufMut;
|
||||||
|
use epee_encoding::{EpeeObject, EpeeValue};
|
||||||
use std::{hash::Hash, net, net::SocketAddr};
|
use std::{hash::Hash, net, net::SocketAddr};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
mod serde_helper;
|
mod serde_helper;
|
||||||
use serde_helper::*;
|
use serde_helper::*;
|
||||||
|
|
||||||
|
@ -33,13 +33,23 @@ pub enum NetZone {
|
||||||
|
|
||||||
/// A network address which can be encoded into the format required
|
/// A network address which can be encoded into the format required
|
||||||
/// to send to other Monero peers.
|
/// to send to other Monero peers.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
#[serde(try_from = "TaggedNetworkAddress")]
|
|
||||||
#[serde(into = "TaggedNetworkAddress")]
|
|
||||||
pub enum NetworkAddress {
|
pub enum NetworkAddress {
|
||||||
Clear(SocketAddr),
|
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 {
|
impl NetworkAddress {
|
||||||
pub fn get_zone(&self) -> NetZone {
|
pub fn get_zone(&self) -> NetZone {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -1,20 +1,53 @@
|
||||||
|
use bytes::Buf;
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use epee_encoding::{epee_object, EpeeObjectBuilder};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::NetworkAddress;
|
use crate::NetworkAddress;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Default)]
|
||||||
pub(crate) struct TaggedNetworkAddress {
|
pub struct TaggedNetworkAddress {
|
||||||
#[serde(rename = "type")]
|
ty: Option<u8>,
|
||||||
ty: u8,
|
addr: Option<AllFieldsNetworkAddress>,
|
||||||
addr: 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)]
|
#[derive(Error, Debug)]
|
||||||
#[error("Invalid network address")]
|
#[error("Invalid network address")]
|
||||||
pub(crate) struct InvalidNetworkAddress;
|
pub struct InvalidNetworkAddress;
|
||||||
|
|
||||||
impl TryFrom<TaggedNetworkAddress> for NetworkAddress {
|
impl TryFrom<TaggedNetworkAddress> for NetworkAddress {
|
||||||
type Error = InvalidNetworkAddress;
|
type Error = InvalidNetworkAddress;
|
||||||
|
@ -22,7 +55,8 @@ impl TryFrom<TaggedNetworkAddress> for NetworkAddress {
|
||||||
fn try_from(value: TaggedNetworkAddress) -> Result<Self, Self::Error> {
|
fn try_from(value: TaggedNetworkAddress) -> Result<Self, Self::Error> {
|
||||||
value
|
value
|
||||||
.addr
|
.addr
|
||||||
.try_into_network_address(value.ty)
|
.ok_or(InvalidNetworkAddress)?
|
||||||
|
.try_into_network_address(value.ty.ok_or(InvalidNetworkAddress)?)
|
||||||
.ok_or(InvalidNetworkAddress)
|
.ok_or(InvalidNetworkAddress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,36 +66,40 @@ impl From<NetworkAddress> for TaggedNetworkAddress {
|
||||||
match value {
|
match value {
|
||||||
NetworkAddress::Clear(addr) => match addr {
|
NetworkAddress::Clear(addr) => match addr {
|
||||||
SocketAddr::V4(addr) => TaggedNetworkAddress {
|
SocketAddr::V4(addr) => TaggedNetworkAddress {
|
||||||
ty: 1,
|
ty: Some(1),
|
||||||
addr: AllFieldsNetworkAddress {
|
addr: Some(AllFieldsNetworkAddress {
|
||||||
m_ip: Some(u32::from_be_bytes(addr.ip().octets())),
|
m_ip: Some(u32::from_be_bytes(addr.ip().octets())),
|
||||||
m_port: Some(addr.port()),
|
m_port: Some(addr.port()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
}),
|
||||||
},
|
},
|
||||||
SocketAddr::V6(addr) => TaggedNetworkAddress {
|
SocketAddr::V6(addr) => TaggedNetworkAddress {
|
||||||
ty: 2,
|
ty: Some(2),
|
||||||
addr: AllFieldsNetworkAddress {
|
addr: Some(AllFieldsNetworkAddress {
|
||||||
addr: Some(addr.ip().octets()),
|
addr: Some(addr.ip().octets()),
|
||||||
m_port: Some(addr.port()),
|
m_port: Some(addr.port()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Default)]
|
||||||
struct AllFieldsNetworkAddress {
|
struct AllFieldsNetworkAddress {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
m_ip: Option<u32>,
|
m_ip: Option<u32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
m_port: Option<u16>,
|
m_port: Option<u16>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
addr: Option<[u8; 16]>,
|
addr: Option<[u8; 16]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
AllFieldsNetworkAddress,
|
||||||
|
m_ip: Option<u32>,
|
||||||
|
m_port: Option<u16>,
|
||||||
|
addr: Option<[u8; 16]>,
|
||||||
|
);
|
||||||
|
|
||||||
impl AllFieldsNetworkAddress {
|
impl AllFieldsNetworkAddress {
|
||||||
fn try_into_network_address(self, ty: u8) -> Option<NetworkAddress> {
|
fn try_into_network_address(self, ty: u8) -> Option<NetworkAddress> {
|
||||||
Some(match ty {
|
Some(match ty {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
//! 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 bytes::{Buf, Bytes, BytesMut};
|
||||||
use levin_cuprate::{
|
use levin_cuprate::{
|
||||||
BucketBuilder, BucketError, LevinBody, LevinCommand as LevinCommandTrait, MessageType,
|
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,
|
ret: impl FnOnce(T) -> Ret,
|
||||||
buf: &[u8],
|
buf: &mut B,
|
||||||
) -> Result<Ret, BucketError> {
|
) -> Result<Ret, BucketError> {
|
||||||
let t = monero_epee_bin_serde::from_bytes(buf)
|
let t = epee_encoding::from_bytes(buf).map_err(|e| BucketError::BodyDecodingError(e.into()))?;
|
||||||
.map_err(|e| BucketError::BodyDecodingError(e.into()))?;
|
|
||||||
Ok(ret(t))
|
Ok(ret(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_message<T: serde::Serialize>(
|
fn build_message<T: epee_encoding::EpeeObject>(
|
||||||
id: LevinCommand,
|
id: LevinCommand,
|
||||||
val: &T,
|
val: T,
|
||||||
builder: &mut BucketBuilder<LevinCommand>,
|
builder: &mut BucketBuilder<LevinCommand>,
|
||||||
) -> Result<(), BucketError> {
|
) -> Result<(), BucketError> {
|
||||||
builder.set_command(id);
|
builder.set_command(id);
|
||||||
builder.set_body(
|
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()))?,
|
.map_err(|e| BucketError::BodyDecodingError(e.into()))?,
|
||||||
);
|
);
|
||||||
Ok(())
|
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;
|
use LevinCommand as C;
|
||||||
|
|
||||||
Ok(match command {
|
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;
|
use LevinCommand as C;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
|
@ -236,7 +237,7 @@ impl ProtocolMessage {
|
||||||
}
|
}
|
||||||
ProtocolMessage::ChainRequest(val) => build_message(C::ChainRequest, val, builder)?,
|
ProtocolMessage::ChainRequest(val) => build_message(C::ChainRequest, val, builder)?,
|
||||||
ProtocolMessage::ChainEntryResponse(val) => {
|
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::NewFluffyBlock(val) => build_message(C::NewFluffyBlock, val, builder)?,
|
||||||
ProtocolMessage::FluffyMissingTransactionsRequest(val) => {
|
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;
|
use LevinCommand as C;
|
||||||
|
|
||||||
Ok(match command {
|
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;
|
use LevinCommand as C;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
|
@ -289,11 +290,11 @@ impl RequestMessage {
|
||||||
RequestMessage::TimedSync(val) => build_message(C::TimedSync, val, builder)?,
|
RequestMessage::TimedSync(val) => build_message(C::TimedSync, val, builder)?,
|
||||||
RequestMessage::Ping => {
|
RequestMessage::Ping => {
|
||||||
builder.set_command(C::Ping);
|
builder.set_command(C::Ping);
|
||||||
builder.set_body(Vec::new());
|
builder.set_body(Bytes::new());
|
||||||
}
|
}
|
||||||
RequestMessage::SupportFlags => {
|
RequestMessage::SupportFlags => {
|
||||||
builder.set_command(C::SupportFlags);
|
builder.set_command(C::SupportFlags);
|
||||||
builder.set_body(Vec::new());
|
builder.set_body(Bytes::new());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
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;
|
use LevinCommand as C;
|
||||||
|
|
||||||
Ok(match command {
|
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;
|
use LevinCommand as C;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
|
@ -375,8 +376,8 @@ impl Message {
|
||||||
impl LevinBody for Message {
|
impl LevinBody for Message {
|
||||||
type Command = LevinCommand;
|
type Command = LevinCommand;
|
||||||
|
|
||||||
fn decode_message(
|
fn decode_message<B: Buf>(
|
||||||
body: &[u8],
|
body: &mut B,
|
||||||
typ: MessageType,
|
typ: MessageType,
|
||||||
command: LevinCommand,
|
command: LevinCommand,
|
||||||
) -> Result<Self, BucketError> {
|
) -> 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 {
|
match self {
|
||||||
Message::Protocol(pro) => {
|
Message::Protocol(pro) => {
|
||||||
builder.set_message_type(MessageType::Notification);
|
builder.set_message_type(MessageType::Notification);
|
||||||
|
|
|
@ -18,12 +18,13 @@
|
||||||
//! 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 serde::{Deserialize, Serialize};
|
use bytes::Bytes;
|
||||||
|
use epee_encoding::epee_object;
|
||||||
|
|
||||||
use super::common::{BasicNodeData, CoreSyncData, PeerListEntryBase, PeerSupportFlags};
|
use super::common::{BasicNodeData, CoreSyncData, PeerListEntryBase, PeerSupportFlags};
|
||||||
|
|
||||||
/// A Handshake Request
|
/// A Handshake Request
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct HandshakeRequest {
|
pub struct HandshakeRequest {
|
||||||
/// Basic Node Data
|
/// Basic Node Data
|
||||||
pub node_data: BasicNodeData,
|
pub node_data: BasicNodeData,
|
||||||
|
@ -31,54 +32,87 @@ pub struct HandshakeRequest {
|
||||||
pub payload_data: CoreSyncData,
|
pub payload_data: CoreSyncData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
HandshakeRequest,
|
||||||
|
node_data: BasicNodeData,
|
||||||
|
payload_data: CoreSyncData,
|
||||||
|
);
|
||||||
|
|
||||||
/// A Handshake Response
|
/// A Handshake Response
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, 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 = "Vec::new")]
|
|
||||||
pub local_peerlist_new: Vec<PeerListEntryBase>,
|
pub local_peerlist_new: Vec<PeerListEntryBase>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
HandshakeResponse,
|
||||||
|
node_data: BasicNodeData,
|
||||||
|
payload_data: CoreSyncData,
|
||||||
|
local_peerlist_new: Vec<PeerListEntryBase>,
|
||||||
|
);
|
||||||
|
|
||||||
/// A TimedSync Request
|
/// A TimedSync Request
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct TimedSyncRequest {
|
pub struct TimedSyncRequest {
|
||||||
/// Core Sync Data
|
/// Core Sync Data
|
||||||
pub payload_data: CoreSyncData,
|
pub payload_data: CoreSyncData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
TimedSyncRequest,
|
||||||
|
payload_data: CoreSyncData,
|
||||||
|
);
|
||||||
|
|
||||||
/// A TimedSync Response
|
/// A TimedSync Response
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct TimedSyncResponse {
|
pub struct TimedSyncResponse {
|
||||||
/// Core Sync Data
|
/// Core Sync Data
|
||||||
pub payload_data: CoreSyncData,
|
pub payload_data: CoreSyncData,
|
||||||
/// PeerList
|
/// PeerList
|
||||||
#[serde(default = "Vec::new")]
|
|
||||||
pub local_peerlist_new: Vec<PeerListEntryBase>,
|
pub local_peerlist_new: Vec<PeerListEntryBase>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
TimedSyncResponse,
|
||||||
|
payload_data: CoreSyncData,
|
||||||
|
local_peerlist_new: Vec<PeerListEntryBase>,
|
||||||
|
);
|
||||||
|
|
||||||
/// 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: Bytes = Bytes::from_static("OK".as_bytes());
|
||||||
|
|
||||||
/// A Ping Response
|
/// A Ping Response
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, 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: Bytes,
|
||||||
/// Peer ID
|
/// Peer ID
|
||||||
pub peer_id: u64,
|
pub peer_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
PingResponse,
|
||||||
|
status: Bytes,
|
||||||
|
peer_id: u64,
|
||||||
|
);
|
||||||
|
|
||||||
/// A Support Flags Response
|
/// A Support Flags Response
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct SupportFlagsResponse {
|
pub struct SupportFlagsResponse {
|
||||||
/// Support Flags
|
/// Support Flags
|
||||||
pub support_flags: PeerSupportFlags,
|
pub support_flags: PeerSupportFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
SupportFlagsResponse,
|
||||||
|
support_flags: PeerSupportFlags as u32,
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
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,
|
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 = monero_epee_bin_serde::from_bytes(bytes).unwrap();
|
let handshake: HandshakeRequest = epee_encoding::from_bytes(&mut &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,
|
||||||
],
|
]
|
||||||
|
.into(),
|
||||||
peer_id: 9671405426614699871,
|
peer_id: 9671405426614699871,
|
||||||
support_flags: PeerSupportFlags::from(1_u32),
|
support_flags: PeerSupportFlags::from(1_u32),
|
||||||
rpc_port: 0,
|
rpc_port: 0,
|
||||||
|
@ -128,9 +163,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 = monero_epee_bin_serde::to_bytes(&handshake).unwrap();
|
let mut encoded_bytes = epee_encoding::to_bytes(handshake.clone()).unwrap();
|
||||||
let handshake_2: HandshakeRequest =
|
let handshake_2: HandshakeRequest = epee_encoding::from_bytes(&mut encoded_bytes).unwrap();
|
||||||
monero_epee_bin_serde::from_bytes(encoded_bytes).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(handshake, handshake_2);
|
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,
|
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 = monero_epee_bin_serde::from_bytes(bytes).unwrap();
|
let handshake: HandshakeResponse = epee_encoding::from_bytes(&mut &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,
|
||||||
],
|
]
|
||||||
|
.into(),
|
||||||
peer_id: 6037804360359455404,
|
peer_id: 6037804360359455404,
|
||||||
support_flags: PeerSupportFlags::from(1_u32),
|
support_flags: PeerSupportFlags::from(1_u32),
|
||||||
rpc_port: 18089,
|
rpc_port: 18089,
|
||||||
|
@ -935,9 +970,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 = monero_epee_bin_serde::to_bytes(&handshake).unwrap();
|
let mut encoded_bytes = epee_encoding::to_bytes(handshake.clone()).unwrap();
|
||||||
let handshake_2: HandshakeResponse =
|
let handshake_2: HandshakeResponse = epee_encoding::from_bytes(&mut encoded_bytes).unwrap();
|
||||||
monero_epee_bin_serde::from_bytes(encoded_bytes).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(handshake, handshake_2);
|
assert_eq!(handshake, handshake_2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,13 @@
|
||||||
|
|
||||||
//! Common types that are used across multiple messages.
|
//! 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::{
|
use crate::NetworkAddress;
|
||||||
serde_helpers::{default_false, default_zero, serde_vec_bytes},
|
|
||||||
NetworkAddress,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[serde(transparent)]
|
|
||||||
pub struct PeerSupportFlags(u32);
|
pub struct PeerSupportFlags(u32);
|
||||||
|
|
||||||
impl From<u32> for PeerSupportFlags {
|
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 {
|
impl PeerSupportFlags {
|
||||||
//const FLUFFY_BLOCKS: u32 = 0b0000_0001;
|
//const FLUFFY_BLOCKS: u32 = 0b0000_0001;
|
||||||
|
|
||||||
|
@ -53,51 +57,65 @@ impl From<u8> for PeerSupportFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Basic Node Data, information on the connected peer
|
/// Basic Node Data, information on the connected peer
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, 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: ByteArray<16>,
|
||||||
/// Peer ID
|
/// Peer ID
|
||||||
pub peer_id: u64,
|
pub peer_id: u64,
|
||||||
/// The Peers Support Flags
|
/// The Peers Support Flags
|
||||||
/// (If this is not in the message the default is 0)
|
/// (If this is not in the message the default is 0)
|
||||||
#[serde(default = "default_zero")]
|
|
||||||
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 = "default_zero")]
|
|
||||||
pub rpc_port: u16,
|
pub rpc_port: u16,
|
||||||
/// RPC Credits Per Hash
|
/// RPC Credits Per Hash
|
||||||
/// (If this is not in the message the default is 0)
|
/// (If this is not in the message the default is 0)
|
||||||
#[serde(default = "default_zero")]
|
|
||||||
pub rpc_credits_per_hash: u32,
|
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
|
/// 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 {
|
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 = "default_zero")]
|
|
||||||
pub cumulative_difficulty_top64: u64,
|
pub cumulative_difficulty_top64: u64,
|
||||||
/// Current Height of the peer
|
/// Current Height of the peer
|
||||||
pub current_height: u64,
|
pub current_height: u64,
|
||||||
/// Pruning Seed of the peer
|
/// Pruning Seed of the peer
|
||||||
/// (If this is not in the message the default is 0)
|
/// (If this is not in the message the default is 0)
|
||||||
#[serde(default = "default_zero")]
|
|
||||||
pub pruning_seed: u32,
|
pub pruning_seed: u32,
|
||||||
/// Hash of the top block
|
/// Hash of the top block
|
||||||
pub top_id: [u8; 32],
|
pub top_id: ByteArray<32>,
|
||||||
/// Version of the top block
|
/// Version of the top block
|
||||||
#[serde(default = "default_zero")]
|
|
||||||
pub top_version: u8,
|
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 {
|
impl CoreSyncData {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
cumulative_difficulty_128: u128,
|
cumulative_difficulty_128: u128,
|
||||||
|
@ -113,7 +131,7 @@ impl CoreSyncData {
|
||||||
cumulative_difficulty_top64,
|
cumulative_difficulty_top64,
|
||||||
current_height,
|
current_height,
|
||||||
pruning_seed,
|
pruning_seed,
|
||||||
top_id,
|
top_id: top_id.into(),
|
||||||
top_version,
|
top_version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,26 +145,32 @@ impl CoreSyncData {
|
||||||
|
|
||||||
/// PeerListEntryBase, information kept on a peer which will be entered
|
/// PeerListEntryBase, information kept on a peer which will be entered
|
||||||
/// in a peer list/store.
|
/// in a peer list/store.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, 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: u64,
|
pub id: u64,
|
||||||
/// The last Time The Peer Was Seen
|
/// The last Time The Peer Was Seen
|
||||||
#[serde(default = "default_zero")]
|
|
||||||
pub last_seen: i64,
|
pub last_seen: i64,
|
||||||
/// The Pruning Seed
|
/// The Pruning Seed
|
||||||
#[serde(default = "default_zero")]
|
|
||||||
pub pruning_seed: u32,
|
pub pruning_seed: u32,
|
||||||
/// The RPC port
|
/// The RPC port
|
||||||
#[serde(default = "default_zero")]
|
|
||||||
pub rpc_port: u16,
|
pub rpc_port: u16,
|
||||||
/// The RPC credits per hash
|
/// The RPC credits per hash
|
||||||
#[serde(default = "default_zero")]
|
|
||||||
pub rpc_credits_per_hash: u32,
|
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 {
|
impl std::hash::Hash for PeerListEntryBase {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
// We only hash the adr so we can look this up in a HashSet.
|
// 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
|
/// 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 {
|
pub struct PrunedTxBlobEntry {
|
||||||
/// The Tx
|
/// The Tx
|
||||||
#[serde(with = "serde_bytes")]
|
pub tx: Bytes,
|
||||||
pub tx: Vec<u8>,
|
|
||||||
/// The Prunable Tx Hash
|
/// The Prunable Tx Hash
|
||||||
pub prunable_hash: [u8; 32],
|
pub prunable_hash: ByteArray<32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
epee_object!(
|
||||||
#[serde(untagged)]
|
PrunedTxBlobEntry,
|
||||||
|
tx: Bytes,
|
||||||
|
prunable_hash: ByteArray<32>,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum TransactionBlobs {
|
pub enum TransactionBlobs {
|
||||||
Pruned(Vec<PrunedTxBlobEntry>),
|
Pruned(Vec<PrunedTxBlobEntry>),
|
||||||
#[serde(with = "serde_vec_bytes")]
|
Normal(Vec<Bytes>),
|
||||||
Normal(Vec<Vec<u8>>),
|
|
||||||
#[serde(skip_serializing)]
|
|
||||||
None,
|
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 {
|
match self {
|
||||||
TransactionBlobs::Normal(txs) => Some(txs),
|
TransactionBlobs::Normal(txs) => Some(txs),
|
||||||
TransactionBlobs::Pruned(_) => None,
|
TransactionBlobs::Pruned(_) => None,
|
||||||
|
@ -202,30 +228,57 @@ impl TransactionBlobs {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
self.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn none() -> TransactionBlobs {
|
|
||||||
TransactionBlobs::None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Block that can contain transactions
|
/// A Block that can contain transactions
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct BlockCompleteEntry {
|
pub struct BlockCompleteEntry {
|
||||||
/// True if tx data is pruned
|
/// True if tx data is pruned
|
||||||
#[serde(default = "default_false")]
|
|
||||||
pub pruned: bool,
|
pub pruned: bool,
|
||||||
/// The Block
|
/// The Block
|
||||||
#[serde(with = "serde_bytes")]
|
pub block: Bytes,
|
||||||
pub block: Vec<u8>,
|
|
||||||
/// The Block Weight/Size
|
/// The Block Weight/Size
|
||||||
#[serde(default = "default_zero")]
|
|
||||||
pub block_weight: u64,
|
pub block_weight: u64,
|
||||||
/// The blocks txs
|
/// The blocks txs
|
||||||
#[serde(skip_serializing_if = "TransactionBlobs::is_empty")]
|
|
||||||
#[serde(default = "TransactionBlobs::none")]
|
|
||||||
pub txs: TransactionBlobs,
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
|
|
@ -18,16 +18,15 @@
|
||||||
//! 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, Serialize};
|
use bytes::Bytes;
|
||||||
use serde_bytes::ByteBuf;
|
|
||||||
|
|
||||||
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 super::common::BlockCompleteEntry;
|
||||||
use crate::serde_helpers::*;
|
|
||||||
|
|
||||||
/// A block that SHOULD have transactions
|
/// A block that SHOULD have transactions
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct NewBlock {
|
pub struct NewBlock {
|
||||||
/// Block with txs
|
/// Block with txs
|
||||||
pub b: BlockCompleteEntry,
|
pub b: BlockCompleteEntry,
|
||||||
|
@ -35,63 +34,80 @@ pub struct NewBlock {
|
||||||
pub current_blockchain_height: u64,
|
pub current_blockchain_height: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
NewBlock,
|
||||||
|
b: BlockCompleteEntry,
|
||||||
|
current_blockchain_height: u64,
|
||||||
|
);
|
||||||
|
|
||||||
/// New Tx Pool Transactions
|
/// New Tx Pool Transactions
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct NewTransactions {
|
pub struct NewTransactions {
|
||||||
/// Tx Blobs
|
/// Tx Blobs
|
||||||
pub txs: Vec<ByteBuf>,
|
pub txs: Vec<Bytes>,
|
||||||
/// Dandelionpp true if fluff - backwards compatible mode is fluff
|
/// Dandelionpp true if fluff - backwards compatible mode is fluff
|
||||||
#[serde(default = "default_true")]
|
|
||||||
pub dandelionpp_fluff: bool,
|
pub dandelionpp_fluff: bool,
|
||||||
/// Padding
|
/// Padding
|
||||||
#[serde(rename = "_")]
|
pub padding: Bytes,
|
||||||
#[serde(with = "serde_bytes")]
|
|
||||||
pub padding: Vec<u8>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
NewTransactions,
|
||||||
|
txs: Vec<Bytes>,
|
||||||
|
dandelionpp_fluff: bool = true,
|
||||||
|
padding("_"): Bytes,
|
||||||
|
);
|
||||||
|
|
||||||
/// A Request For Blocks
|
/// A Request For Blocks
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct GetObjectsRequest {
|
pub struct GetObjectsRequest {
|
||||||
/// Block hashes we want
|
/// Block hashes we want
|
||||||
#[serde(default = "Vec::new")]
|
pub blocks: ByteArrayVec<32>,
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
|
||||||
#[serde(with = "container_as_blob")]
|
|
||||||
pub blocks: Vec<[u8; 32]>,
|
|
||||||
/// Pruned
|
/// Pruned
|
||||||
#[serde(default = "default_false")]
|
|
||||||
pub pruned: bool,
|
pub pruned: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
GetObjectsRequest,
|
||||||
|
blocks: ByteArrayVec<32>,
|
||||||
|
pruned: bool = false,
|
||||||
|
);
|
||||||
|
|
||||||
/// A Blocks Response
|
/// A Blocks Response
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct GetObjectsResponse {
|
pub struct GetObjectsResponse {
|
||||||
/// Blocks
|
/// Blocks
|
||||||
// We dont need to give this a default value as there always is at least 1 block
|
|
||||||
pub blocks: Vec<BlockCompleteEntry>,
|
pub blocks: Vec<BlockCompleteEntry>,
|
||||||
/// Missed IDs
|
/// Missed IDs
|
||||||
#[serde(default = "Vec::new")]
|
pub missed_ids: ByteArrayVec<32>,
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
|
||||||
#[serde(with = "container_as_blob")]
|
|
||||||
pub missed_ids: Vec<[u8; 32]>,
|
|
||||||
/// The height of the peers blockchain
|
/// The height of the peers blockchain
|
||||||
pub current_blockchain_height: u64,
|
pub current_blockchain_height: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
GetObjectsResponse,
|
||||||
|
blocks: Vec<BlockCompleteEntry>,
|
||||||
|
missed_ids: ByteArrayVec<32>,
|
||||||
|
current_blockchain_height: u64,
|
||||||
|
);
|
||||||
|
|
||||||
/// A Chain Request
|
/// A Chain Request
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct ChainRequest {
|
pub struct ChainRequest {
|
||||||
/// Block IDs
|
/// Block IDs
|
||||||
#[serde(default = "Vec::new")]
|
pub block_ids: ByteArrayVec<32>,
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
|
||||||
#[serde(with = "container_as_blob")]
|
|
||||||
pub block_ids: Vec<[u8; 32]>,
|
|
||||||
/// Prune
|
/// Prune
|
||||||
#[serde(default = "default_false")]
|
|
||||||
pub prune: bool,
|
pub prune: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
ChainRequest,
|
||||||
|
block_ids: ByteArrayVec<32>,
|
||||||
|
prune: bool = false,
|
||||||
|
);
|
||||||
|
|
||||||
/// A Chain Response
|
/// A Chain Response
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct ChainResponse {
|
pub struct ChainResponse {
|
||||||
/// Start Height
|
/// Start Height
|
||||||
pub start_height: u64,
|
pub start_height: u64,
|
||||||
|
@ -100,26 +116,28 @@ pub struct ChainResponse {
|
||||||
/// Cumulative Difficulty Low
|
/// Cumulative Difficulty Low
|
||||||
pub cumulative_difficulty: u64,
|
pub cumulative_difficulty: u64,
|
||||||
/// Cumulative Difficulty High
|
/// Cumulative Difficulty High
|
||||||
#[serde(default = "default_zero")]
|
|
||||||
pub cumulative_difficulty_top64: u64,
|
pub cumulative_difficulty_top64: u64,
|
||||||
/// Block IDs
|
/// Block IDs
|
||||||
#[serde(default = "Vec::new")]
|
pub m_block_ids: ByteArrayVec<32>,
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
|
||||||
#[serde(with = "container_as_blob")]
|
|
||||||
pub m_block_ids: Vec<[u8; 32]>,
|
|
||||||
/// Block Weights
|
/// Block Weights
|
||||||
#[serde(default = "Vec::new")]
|
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
|
||||||
#[serde(with = "container_as_blob")]
|
|
||||||
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(default = "Vec::new")]
|
pub first_block: Bytes,
|
||||||
#[serde(with = "serde_bytes")]
|
|
||||||
pub first_block: Vec<u8>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
/// A Block that doesn't have transactions unless requested
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct NewFluffyBlock {
|
pub struct NewFluffyBlock {
|
||||||
/// Block which might have transactions
|
/// Block which might have transactions
|
||||||
pub b: BlockCompleteEntry,
|
pub b: BlockCompleteEntry,
|
||||||
|
@ -127,30 +145,42 @@ pub struct NewFluffyBlock {
|
||||||
pub current_blockchain_height: u64,
|
pub current_blockchain_height: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
NewFluffyBlock,
|
||||||
|
b: BlockCompleteEntry,
|
||||||
|
current_blockchain_height: u64,
|
||||||
|
);
|
||||||
|
|
||||||
/// A request for Txs we are missing from our TxPool
|
/// 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 {
|
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: ByteArray<32>,
|
||||||
/// The current blockchain height
|
/// The current blockchain height
|
||||||
pub current_blockchain_height: u64,
|
pub current_blockchain_height: u64,
|
||||||
/// The Tx Indices
|
/// 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>,
|
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
|
/// TxPoolCompliment
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct GetTxPoolCompliment {
|
pub struct GetTxPoolCompliment {
|
||||||
/// Tx Hashes
|
/// Tx Hashes
|
||||||
#[serde(default = "Vec::new")]
|
pub hashes: ByteArrayVec<32>,
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
|
||||||
#[serde(with = "container_as_blob")]
|
|
||||||
pub hashes: Vec<[u8; 32]>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee_object!(
|
||||||
|
GetTxPoolCompliment,
|
||||||
|
hashes: ByteArrayVec<32>,
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
@ -667,13 +697,13 @@ mod tests {
|
||||||
248, 248, 91, 110, 107, 144, 12, 175, 253, 21, 121, 28,
|
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());
|
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 =
|
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);
|
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,
|
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 = 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 mut encoded_bytes = epee_encoding::to_bytes(fluffy_block.clone()).unwrap();
|
||||||
let fluffy_block_2: NewFluffyBlock =
|
let fluffy_block_2: NewFluffyBlock = epee_encoding::from_bytes(&mut encoded_bytes).unwrap();
|
||||||
monero_epee_bin_serde::from_bytes(encoded_bytes).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(fluffy_block, fluffy_block_2);
|
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 {
|
let our_basic_node_data_1 = BasicNodeData {
|
||||||
my_port: 0,
|
my_port: 0,
|
||||||
network_id: Network::Mainnet.network_id(),
|
network_id: Network::Mainnet.network_id().into(),
|
||||||
peer_id: 87980,
|
peer_id: 87980,
|
||||||
// TODO: This fails if the support flags are empty (0)
|
// TODO: This fails if the support flags are empty (0)
|
||||||
support_flags: PeerSupportFlags::from(1_u32),
|
support_flags: PeerSupportFlags::from(1_u32),
|
||||||
|
@ -113,7 +113,7 @@ async fn handshake() {
|
||||||
|
|
||||||
let our_basic_node_data = BasicNodeData {
|
let our_basic_node_data = BasicNodeData {
|
||||||
my_port: 0,
|
my_port: 0,
|
||||||
network_id: Network::Mainnet.network_id(),
|
network_id: Network::Mainnet.network_id().into(),
|
||||||
peer_id: 87980,
|
peer_id: 87980,
|
||||||
support_flags: PeerSupportFlags::from(1_u32),
|
support_flags: PeerSupportFlags::from(1_u32),
|
||||||
rpc_port: 0,
|
rpc_port: 0,
|
||||||
|
|
Loading…
Reference in a new issue