fix(miniprotocols): Handle regression related to multi-msg payloads (#76)
This commit is contained in:
parent
d1caf79198
commit
3234523c83
4 changed files with 79 additions and 39 deletions
|
|
@ -8,10 +8,7 @@ use pallas::network::{
|
||||||
multiplexer::Multiplexer,
|
multiplexer::Multiplexer,
|
||||||
};
|
};
|
||||||
|
|
||||||
use pallas::{
|
use pallas::network::miniprotocols::blockfetch::{BatchClient, Observer};
|
||||||
ledger::primitives::{alonzo::*, Fragment},
|
|
||||||
network::miniprotocols::blockfetch::{BatchClient, Observer},
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
|
|
||||||
|
|
@ -22,7 +19,6 @@ impl Observer for BlockPrinter {
|
||||||
fn on_block_received(&self, body: Vec<u8>) -> Result<(), Box<dyn std::error::Error>> {
|
fn on_block_received(&self, body: Vec<u8>) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("{}", hex::encode(&body));
|
println!("{}", hex::encode(&body));
|
||||||
println!("----------");
|
println!("----------");
|
||||||
BlockWrapper::decode_fragment(&body[..])?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -42,13 +38,13 @@ fn main() {
|
||||||
|
|
||||||
let range = (
|
let range = (
|
||||||
Point::Specific(
|
Point::Specific(
|
||||||
4492794,
|
97,
|
||||||
hex::decode("5c196e7394ace0449ba5a51c919369699b13896e97432894b4f0354dce8670b6")
|
hex::decode("cf7fa60bbd210273d79fa48d11ab1d141242af32b231cc40ce3411230a8d3c61")
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
),
|
),
|
||||||
Point::Specific(
|
Point::Specific(
|
||||||
4492794,
|
99,
|
||||||
hex::decode("5c196e7394ace0449ba5a51c919369699b13896e97432894b4f0354dce8670b6")
|
hex::decode("a52cca923a67326ea9c409e958a17a77990be72f3607625ec5b3d456202e223e")
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,9 @@
|
||||||
use minicbor::encode::Write;
|
|
||||||
|
|
||||||
/// Shared re-export of minicbor lib across all Pallas
|
/// Shared re-export of minicbor lib across all Pallas
|
||||||
pub use minicbor;
|
pub use minicbor;
|
||||||
|
|
||||||
/// Round-trip friendly common helper structs
|
/// Round-trip friendly common helper structs
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
pub trait Fragment: Sized {
|
pub trait Fragment: Sized + for<'b> minicbor::Decode<'b> + minicbor::Encode {}
|
||||||
fn read_cbor(buffer: &[u8]) -> Result<Self, minicbor::decode::Error>;
|
|
||||||
fn write_cbor<W: Write>(&self, write: W) -> Result<(), minicbor::encode::Error<W::Error>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Fragment for T
|
impl<T> Fragment for T where T: for<'b> minicbor::Decode<'b> + minicbor::Encode + Sized {}
|
||||||
where
|
|
||||||
T: for<'b> minicbor::Decode<'b> + minicbor::Encode,
|
|
||||||
{
|
|
||||||
fn read_cbor(buffer: &[u8]) -> Result<Self, minicbor::decode::Error> {
|
|
||||||
minicbor::decode(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_cbor<W: Write>(&self, write: W) -> Result<(), minicbor::encode::Error<W::Error>> {
|
|
||||||
minicbor::encode(self, write)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
pub use crate::payloads::*;
|
pub use crate::payloads::*;
|
||||||
use pallas_codec::Fragment;
|
use pallas_codec::{minicbor, Fragment};
|
||||||
use pallas_multiplexer::{Channel, Payload};
|
use pallas_multiplexer::{Channel, Payload};
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
@ -66,7 +66,7 @@ pub trait MachineOutput {
|
||||||
impl MachineOutput for Sender<Payload> {
|
impl MachineOutput for Sender<Payload> {
|
||||||
fn send_msg(&self, data: &impl Fragment) -> Result<(), Box<dyn std::error::Error>> {
|
fn send_msg(&self, data: &impl Fragment) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut payload = Vec::new();
|
let mut payload = Vec::new();
|
||||||
data.write_cbor(&mut payload)?;
|
minicbor::encode(data, &mut payload)?;
|
||||||
self.send(payload)?;
|
self.send(payload)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -92,6 +92,7 @@ where
|
||||||
let Channel(tx, rx) = channel;
|
let Channel(tx, rx) = channel;
|
||||||
|
|
||||||
let mut agent = agent;
|
let mut agent = agent;
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
|
||||||
while !agent.is_done() {
|
while !agent.is_done() {
|
||||||
log::debug!("evaluating agent {:?}", agent);
|
log::debug!("evaluating agent {:?}", agent);
|
||||||
|
|
@ -101,8 +102,6 @@ where
|
||||||
agent = agent.send_next(tx)?;
|
agent = agent.send_next(tx)?;
|
||||||
}
|
}
|
||||||
false => {
|
false => {
|
||||||
let mut buffer = Vec::new();
|
|
||||||
|
|
||||||
let msg = read_until_full_msg::<T::Message>(&mut buffer, rx).unwrap();
|
let msg = read_until_full_msg::<T::Message>(&mut buffer, rx).unwrap();
|
||||||
log::trace!("procesing inbound msg: {:?}", msg);
|
log::trace!("procesing inbound msg: {:?}", msg);
|
||||||
agent = agent.receive_next(msg)?;
|
agent = agent.receive_next(msg)?;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use pallas_codec::Fragment;
|
use pallas_codec::{minicbor, Fragment};
|
||||||
use pallas_multiplexer::Payload;
|
use pallas_multiplexer::Payload;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
|
|
||||||
pub type Error = Box<dyn std::error::Error>;
|
pub type Error = Box<dyn std::error::Error>;
|
||||||
|
|
||||||
enum Decoding<M> {
|
enum Decoding<M> {
|
||||||
Done(M),
|
Done(M, usize),
|
||||||
NotEnoughData,
|
NotEnoughData,
|
||||||
UnexpectedError(Error),
|
UnexpectedError(Error),
|
||||||
}
|
}
|
||||||
|
|
@ -14,15 +14,17 @@ fn try_decode_message<M>(buffer: &[u8]) -> Decoding<M>
|
||||||
where
|
where
|
||||||
M: Fragment,
|
M: Fragment,
|
||||||
{
|
{
|
||||||
let maybe_msg: Result<M, _> = M::read_cbor(buffer);
|
let mut decoder = minicbor::Decoder::new(buffer);
|
||||||
|
let maybe_msg = decoder.decode();
|
||||||
|
|
||||||
match maybe_msg {
|
match maybe_msg {
|
||||||
Ok(msg) => Decoding::Done(msg),
|
Ok(msg) => Decoding::Done(msg, decoder.position()),
|
||||||
Err(err) if err.is_end_of_input() => Decoding::NotEnoughData,
|
Err(err) if err.is_end_of_input() => Decoding::NotEnoughData,
|
||||||
Err(err) => Decoding::UnexpectedError(Box::new(err)),
|
Err(err) => Decoding::UnexpectedError(Box::new(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads from the receiver until a complete message is found
|
||||||
pub fn read_until_full_msg<M>(
|
pub fn read_until_full_msg<M>(
|
||||||
buffer: &mut Vec<u8>,
|
buffer: &mut Vec<u8>,
|
||||||
receiver: &mut Receiver<Payload>,
|
receiver: &mut Receiver<Payload>,
|
||||||
|
|
@ -30,14 +32,73 @@ pub fn read_until_full_msg<M>(
|
||||||
where
|
where
|
||||||
M: Fragment,
|
M: Fragment,
|
||||||
{
|
{
|
||||||
let chunk = receiver.recv()?;
|
// do an eager reading if buffer is empty, no point in going through the error
|
||||||
buffer.extend(chunk);
|
// handling
|
||||||
|
if buffer.is_empty() {
|
||||||
|
let chunk = receiver.recv()?;
|
||||||
|
buffer.extend(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
let decoding = try_decode_message::<M>(buffer);
|
let decoding = try_decode_message::<M>(buffer);
|
||||||
|
|
||||||
match decoding {
|
match decoding {
|
||||||
Decoding::Done(msg) => Ok(msg),
|
Decoding::Done(msg, pos) => {
|
||||||
|
buffer.drain(0..pos);
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
Decoding::UnexpectedError(err) => Err(err),
|
Decoding::UnexpectedError(err) => Err(err),
|
||||||
Decoding::NotEnoughData => read_until_full_msg::<M>(buffer, receiver),
|
Decoding::NotEnoughData => {
|
||||||
|
let chunk = receiver.recv()?;
|
||||||
|
buffer.extend(chunk);
|
||||||
|
|
||||||
|
read_until_full_msg::<M>(buffer, receiver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::read_until_full_msg;
|
||||||
|
use pallas_codec::minicbor;
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_messages_in_same_payload() {
|
||||||
|
let mut input = Vec::new();
|
||||||
|
let in_part1 = (1u8, 2u8, 3u8);
|
||||||
|
let in_part2 = (6u8, 5u8, 4u8);
|
||||||
|
|
||||||
|
minicbor::encode(in_part1, &mut input).unwrap();
|
||||||
|
minicbor::encode(in_part2, &mut input).unwrap();
|
||||||
|
|
||||||
|
let (tx, mut rx) = channel();
|
||||||
|
tx.send(input).unwrap();
|
||||||
|
|
||||||
|
let mut output = Vec::new();
|
||||||
|
let out_part1 = read_until_full_msg::<(u8, u8, u8)>(&mut output, &mut rx).unwrap();
|
||||||
|
let out_part2 = read_until_full_msg::<(u8, u8, u8)>(&mut output, &mut rx).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(in_part1, out_part1);
|
||||||
|
assert_eq!(in_part2, out_part2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fragmented_message_in_multiple_payload() {
|
||||||
|
let mut input = Vec::new();
|
||||||
|
let msg = (11u8, 12u8, 13u8, 14u8, 15u8, 16u8, 17u8);
|
||||||
|
minicbor::encode(msg, &mut input).unwrap();
|
||||||
|
|
||||||
|
let (tx, mut rx) = channel();
|
||||||
|
|
||||||
|
while !input.is_empty() {
|
||||||
|
let chunk = Vec::from(input.drain(0..2).as_slice());
|
||||||
|
tx.send(chunk).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut output = Vec::new();
|
||||||
|
let out_msg =
|
||||||
|
read_until_full_msg::<(u8, u8, u8, u8, u8, u8, u8)>(&mut output, &mut rx).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(msg, out_msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue