Framed encoding serde POC
This commit is contained in:
@@ -27,6 +27,9 @@ pub enum SphinxCodecError {
|
||||
|
||||
#[error("could not convert to bytes")]
|
||||
ToBytes,
|
||||
|
||||
#[error("could not convert to bytes")]
|
||||
FromBytes,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -70,6 +73,8 @@ impl Encoder<FramedNymPacket> for SphinxCodec {
|
||||
type Error = SphinxCodecError;
|
||||
|
||||
fn encode(&mut self, item: FramedNymPacket, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
let encoded_size = bincode::serialized_size(&item).unwrap() as u32;
|
||||
dst.put_u32(encoded_size);
|
||||
dst.put(
|
||||
bincode::serialize(&item)
|
||||
.map_err(|_| SphinxCodecError::ToBytes)?
|
||||
@@ -98,33 +103,58 @@ impl Decoder for SphinxCodec {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// because header is so small and simple it makes no point in trying to cache
|
||||
// this result. It will be just simpler to re-decode it
|
||||
let header = match Header::decode(src)? {
|
||||
Some(header) => header,
|
||||
None => return Ok(None), // we have some data but not enough to get header back
|
||||
let next_size = if let Some(size) = src.get(..4) {
|
||||
u32::from_be_bytes(size.try_into().unwrap()) as usize
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let sphinx_packet_size = header.packet_size.size();
|
||||
let frame_len = header.size() + sphinx_packet_size;
|
||||
|
||||
if src.len() < frame_len {
|
||||
// we don't have enough bytes to read the rest of frame
|
||||
src.reserve(sphinx_packet_size);
|
||||
// let next_size = src.get_u64() as usize;
|
||||
if next_size == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if let Some(next_packet) = src.get(4..next_size + 4) {
|
||||
match bincode::deserialize::<Self::Item>(next_packet) {
|
||||
Ok(packet) => {
|
||||
src.advance(next_size + 4);
|
||||
return Ok(Some(packet));
|
||||
}
|
||||
Err(_) => {
|
||||
println!("Could not decode packet");
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
|
||||
// because header is so small and simple it makes no point in trying to cache
|
||||
// this result. It will be just simpler to re-decode it
|
||||
// let header = match Header::decode(src)? {
|
||||
// Some(header) => header,
|
||||
// None => return Ok(None), // we have some data but not enough to get header back
|
||||
// };
|
||||
|
||||
// let sphinx_packet_size = framed_packet.header.packet_size.size();
|
||||
// let frame_len = framed_packet.header.size() + sphinx_packet_size;
|
||||
|
||||
// if src.len() < frame_len {
|
||||
// // we don't have enough bytes to read the rest of frame
|
||||
// src.reserve(sphinx_packet_size);
|
||||
// return Ok(None);
|
||||
// }
|
||||
|
||||
// advance buffer past the header - at this point we have enough bytes
|
||||
src.advance(header.size());
|
||||
let sphinx_packet_bytes = src.split_to(sphinx_packet_size);
|
||||
// src.advance(header.size());
|
||||
// let sphinx_packet_bytes = src.split_to(sphinx_packet_size);
|
||||
|
||||
// here it could be debatable whether stream is corrupt or not,
|
||||
// but let's go with the safer approach and assume it is.
|
||||
let packet = SphinxPacket::from_bytes(&sphinx_packet_bytes)?;
|
||||
let nymsphinx_packet = FramedNymPacket {
|
||||
header,
|
||||
packet: NymPacket::Sphinx(packet),
|
||||
};
|
||||
// let packet = SphinxPacket::from_bytes(&sphinx_packet_bytes)?;
|
||||
// let nymsphinx_packet = FramedNymPacket {
|
||||
// header,
|
||||
// packet: NymPacket::Sphinx(packet),
|
||||
// };
|
||||
|
||||
// As per docs:
|
||||
// Before returning from the function, implementations should ensure that the buffer
|
||||
@@ -136,24 +166,24 @@ impl Decoder for SphinxCodec {
|
||||
// reserve for that.
|
||||
// we also assume the next packet coming from the same client will use exactly the same versioning
|
||||
// as the current packet
|
||||
let mut allocate_for_next_packet = header.size() + PacketSize::AckPacket.size();
|
||||
if !src.is_empty() {
|
||||
match Header::decode(src) {
|
||||
Ok(Some(next_header)) => {
|
||||
allocate_for_next_packet = next_header.size() + next_header.packet_size.size();
|
||||
}
|
||||
Ok(None) => {
|
||||
// we don't have enough information to know how much to reserve, fallback to the ack case
|
||||
}
|
||||
// let mut allocate_for_next_packet = framed_packet.header.size() + PacketSize::AckPacket.size();
|
||||
// if !src.is_empty() {
|
||||
// match Header::decode(src) {
|
||||
// Ok(Some(next_header)) => {
|
||||
// allocate_for_next_packet = next_header.size() + next_header.packet_size.size();
|
||||
// }
|
||||
// Ok(None) => {
|
||||
// // we don't have enough information to know how much to reserve, fallback to the ack case
|
||||
// }
|
||||
|
||||
// the next frame will be malformed but let's leave handling the error to the next
|
||||
// call to 'decode', as presumably, the current sphinx packet is still valid
|
||||
Err(_) => return Ok(Some(nymsphinx_packet)),
|
||||
};
|
||||
}
|
||||
src.reserve(allocate_for_next_packet);
|
||||
// // the next frame will be malformed but let's leave handling the error to the next
|
||||
// // call to 'decode', as presumably, the current sphinx packet is still valid
|
||||
// Err(_) => return Ok(Some(framed_packet)),
|
||||
// };
|
||||
// }
|
||||
// src.reserve(allocate_for_next_packet);
|
||||
|
||||
Ok(Some(nymsphinx_packet))
|
||||
// Ok(Some(framed_packet))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +237,7 @@ mod packet_encoding {
|
||||
|
||||
let packet = FramedNymPacket {
|
||||
header,
|
||||
packet: NymPacket::Sphinx(sphinx_packet),
|
||||
packet: Some(NymPacket::Sphinx(sphinx_packet)),
|
||||
};
|
||||
|
||||
let mut bytes = BytesMut::new();
|
||||
@@ -215,7 +245,7 @@ mod packet_encoding {
|
||||
let decoded = SphinxCodec.decode(&mut bytes).unwrap().unwrap();
|
||||
|
||||
assert_eq!(decoded.header, header);
|
||||
assert_eq!(decoded.packet.to_bytes().unwrap(), sphinx_bytes)
|
||||
assert_eq!(decoded.packet.unwrap().to_bytes().unwrap(), sphinx_bytes)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -295,16 +325,18 @@ mod packet_encoding {
|
||||
packet_size: Default::default(),
|
||||
packet_mode: Default::default(),
|
||||
},
|
||||
packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())),
|
||||
packet: Some(NymPacket::Sphinx(make_valid_sphinx_packet(
|
||||
Default::default(),
|
||||
))),
|
||||
};
|
||||
|
||||
let mut bytes = BytesMut::new();
|
||||
SphinxCodec.encode(packet, &mut bytes).unwrap();
|
||||
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
|
||||
assert_eq!(
|
||||
bytes.capacity(),
|
||||
Header::LEGACY_SIZE + PacketSize::AckPacket.size()
|
||||
);
|
||||
// assert_eq!(
|
||||
// bytes.capacity(),
|
||||
// Header::LEGACY_SIZE + PacketSize::AckPacket.size()
|
||||
// );
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -312,16 +344,18 @@ mod packet_encoding {
|
||||
// if full frame is used exactly, there should be enough space for header + ack packet
|
||||
let packet = FramedNymPacket {
|
||||
header: Header::default(),
|
||||
packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())),
|
||||
packet: Some(NymPacket::Sphinx(make_valid_sphinx_packet(
|
||||
Default::default(),
|
||||
))),
|
||||
};
|
||||
|
||||
let mut bytes = BytesMut::new();
|
||||
SphinxCodec.encode(packet, &mut bytes).unwrap();
|
||||
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
|
||||
assert_eq!(
|
||||
bytes.capacity(),
|
||||
Header::VERSIONED_SIZE + PacketSize::AckPacket.size()
|
||||
);
|
||||
// assert_eq!(
|
||||
// bytes.capacity(),
|
||||
// Header::VERSIONED_SIZE + PacketSize::AckPacket.size()
|
||||
// );
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -342,7 +376,9 @@ mod packet_encoding {
|
||||
packet_size: Default::default(),
|
||||
packet_mode: Default::default(),
|
||||
},
|
||||
packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())),
|
||||
packet: Some(NymPacket::Sphinx(make_valid_sphinx_packet(
|
||||
Default::default(),
|
||||
))),
|
||||
};
|
||||
|
||||
let mut bytes = BytesMut::new();
|
||||
@@ -351,7 +387,7 @@ mod packet_encoding {
|
||||
bytes.put_u8(PacketMode::default() as u8);
|
||||
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
|
||||
|
||||
assert!(bytes.capacity() >= Header::LEGACY_SIZE + packet_size.size())
|
||||
// assert!(bytes.capacity() >= Header::LEGACY_SIZE + packet_size.size())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,7 +405,9 @@ mod packet_encoding {
|
||||
for packet_size in packet_sizes {
|
||||
let first_packet = FramedNymPacket {
|
||||
header: Header::default(),
|
||||
packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())),
|
||||
packet: Some(NymPacket::Sphinx(make_valid_sphinx_packet(
|
||||
Default::default(),
|
||||
))),
|
||||
};
|
||||
|
||||
let mut bytes = BytesMut::new();
|
||||
@@ -379,7 +417,7 @@ mod packet_encoding {
|
||||
bytes.put_u8(PacketMode::default() as u8);
|
||||
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
|
||||
|
||||
assert!(bytes.capacity() >= Header::VERSIONED_SIZE + packet_size.size())
|
||||
// assert!(bytes.capacity() >= Header::VERSIONED_SIZE + packet_size.size())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -388,12 +426,16 @@ mod packet_encoding {
|
||||
fn can_decode_two_packets_immediately() {
|
||||
let packet1 = FramedNymPacket {
|
||||
header: Header::default(),
|
||||
packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())),
|
||||
packet: Some(NymPacket::Sphinx(make_valid_sphinx_packet(
|
||||
Default::default(),
|
||||
))),
|
||||
};
|
||||
|
||||
let packet2 = FramedNymPacket {
|
||||
header: Header::default(),
|
||||
packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())),
|
||||
packet: Some(NymPacket::Sphinx(make_valid_sphinx_packet(
|
||||
Default::default(),
|
||||
))),
|
||||
};
|
||||
|
||||
let mut bytes = BytesMut::new();
|
||||
@@ -410,28 +452,38 @@ mod packet_encoding {
|
||||
fn can_decode_two_packets_in_separate_calls() {
|
||||
let packet1 = FramedNymPacket {
|
||||
header: Header::default(),
|
||||
packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())),
|
||||
packet: Some(NymPacket::Sphinx(make_valid_sphinx_packet(
|
||||
Default::default(),
|
||||
))),
|
||||
};
|
||||
|
||||
let packet2 = FramedNymPacket {
|
||||
header: Header::default(),
|
||||
packet: NymPacket::Sphinx(make_valid_sphinx_packet(Default::default())),
|
||||
packet: Some(NymPacket::Sphinx(make_valid_sphinx_packet(
|
||||
Default::default(),
|
||||
))),
|
||||
};
|
||||
|
||||
let mut bytes = BytesMut::new();
|
||||
let mut bytes_tmp = BytesMut::new();
|
||||
|
||||
println!("{}", bytes.len());
|
||||
SphinxCodec.encode(packet1, &mut bytes).unwrap();
|
||||
println!("{}", bytes.len());
|
||||
SphinxCodec.encode(packet2, &mut bytes_tmp).unwrap();
|
||||
println!("{}", bytes.len());
|
||||
|
||||
let tmp = bytes_tmp.split_off(100);
|
||||
bytes.put(bytes_tmp);
|
||||
println!("{}", bytes.len());
|
||||
|
||||
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
|
||||
println!("{}", bytes.len());
|
||||
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_none());
|
||||
println!("{}", bytes.len());
|
||||
|
||||
bytes.put(tmp);
|
||||
|
||||
println!("{}", bytes.len());
|
||||
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_some());
|
||||
assert!(SphinxCodec.decode(&mut bytes).unwrap().is_none());
|
||||
}
|
||||
|
||||
@@ -7,16 +7,16 @@ use nym_sphinx_params::packet_sizes::PacketSize;
|
||||
use nym_sphinx_params::packet_version::PacketVersion;
|
||||
use nym_sphinx_params::PacketMode;
|
||||
use nym_sphinx_types::NymPacket;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct FramedNymPacket {
|
||||
/// Contains any metadata helping receiver to handle the underlying packet.
|
||||
pub(crate) header: Header,
|
||||
|
||||
/// The actual SphinxPacket being sent.
|
||||
pub(crate) packet: NymPacket,
|
||||
pub(crate) packet: Option<NymPacket>,
|
||||
}
|
||||
|
||||
impl FramedNymPacket {
|
||||
@@ -31,7 +31,7 @@ impl FramedNymPacket {
|
||||
packet_size,
|
||||
packet_mode,
|
||||
},
|
||||
packet,
|
||||
packet: Some(packet),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ impl FramedNymPacket {
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> NymPacket {
|
||||
self.packet
|
||||
self.packet.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,21 @@ pub enum NymPacket {
|
||||
Outfox(OutfoxPacket),
|
||||
}
|
||||
|
||||
impl fmt::Debug for NymPacket {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self {
|
||||
NymPacket::Sphinx(packet) => f
|
||||
.debug_struct("NymPacket::Sphinx")
|
||||
.field("len", &packet.len())
|
||||
.finish(),
|
||||
NymPacket::Outfox(packet) => f
|
||||
.debug_struct("NymPacket::Outfox")
|
||||
.field("len", &packet.len())
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NymPacket {
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
|
||||
Reference in New Issue
Block a user